From: Alex Lu <[email protected]>
Add the Realtek Bluetooth profile profiling support to create
profile information, which helps the firmware optimize transfer
priority and balance the transmissions for multiple profiles.
Signed-off-by: Alex Lu <[email protected]>
Signed-off-by: Larry Finger <[email protected]>
---
drivers/bluetooth/Kconfig | 16 +
drivers/bluetooth/Makefile | 1 +
drivers/bluetooth/btusb.c | 12 +
drivers/bluetooth/rtl_btpf.c | 1249 ++++++++++++++++++++++++++++++++++++++++++
drivers/bluetooth/rtl_btpf.h | 184 +++++++
5 files changed, 1462 insertions(+)
create mode 100644 drivers/bluetooth/rtl_btpf.c
create mode 100644 drivers/bluetooth/rtl_btpf.h
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 3cc9bff..354f852 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -14,6 +14,9 @@ config BT_RTL
tristate
select FW_LOADER
+config BT_RTL_BTPF
+ tristate
+
config BT_QCA
tristate
select FW_LOADER
@@ -52,6 +55,19 @@ config BT_HCIBTUSB_RTL
Say Y here to compile support for Realtek protocol.
+config BT_HCIBTUSB_RTL_BTPF
+ bool "Realtek profiling support"
+ depends on BT_HCIBTUSB && BT_RTL
+ select BT_RTL_BTPF
+ default y
+ help
+ This parameter adds Realtek Bluetooth profile profiling support
+ that enables the gathering of profile information, which helps
+ the firmware optimize transfer priority and balance the transmissions
+ for multiple profiles.
+
+ Say Y here to compile support for Realtek profiling.
+
config BT_HCIBTSDIO
tristate "HCI SDIO driver"
depends on MMC
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 8062718..a3dd8a4 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
+obj-$(CONFIG_BT_RTL_BTPF) += rtl_btpf.o
obj-$(CONFIG_BT_QCA) += btqca.o
btmrvl-y := btmrvl_main.o
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 2f633df..bc1c923 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -33,6 +33,10 @@
#include "btbcm.h"
#include "btrtl.h"
+#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
+#include "rtl_btpf.h"
+#endif
+
#define VERSION "0.8"
static bool disable_scofix;
@@ -3023,6 +3027,10 @@ static int btusb_probe(struct usb_interface *intf,
usb_set_intfdata(intf, data);
+#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
+ rtl_btpf_init();
+#endif
+
return 0;
}
@@ -3045,6 +3053,10 @@ static void btusb_disconnect(struct usb_interface *intf)
if (data->diag)
usb_set_intfdata(data->diag, NULL);
+#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
+ rtl_btpf_deinit();
+#endif
+
hci_unregister_dev(hdev);
if (intf == data->intf) {
diff --git a/drivers/bluetooth/rtl_btpf.c b/drivers/bluetooth/rtl_btpf.c
new file mode 100644
index 0000000..a2d19b6
--- /dev/null
+++ b/drivers/bluetooth/rtl_btpf.c
@@ -0,0 +1,1249 @@
+/*
+ *
+ * Realtek Bluetooth Profile profiling driver
+ *
+ * Copyright (C) 2015 Realtek Semiconductor Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/usb.h>
+#include <linux/dcache.h>
+#include <linux/version.h>
+#include <linux/skbuff.h>
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/hci_mon.h>
+#include "rtl_btpf.h"
+
+#define VERSION "0.1"
+
+#define BTPF_CMD_MAXLEN 64
+
+
+static struct rtl_btpf *rtl_btpf;
+
+static int psm_to_profile(u16 psm)
+{
+ switch (psm) {
+ case PSM_AVCTP:
+ case PSM_SDP:
+ return -1; /* ignore */
+
+ case PSM_HID:
+ case PSM_HID_INT:
+ return PROFILE_HID;
+
+ case PSM_AVDTP:
+ return PROFILE_A2DP;
+
+ case PSM_PAN:
+ case PSM_OPP:
+ case PSM_FTP:
+ case PSM_BIP:
+ case PSM_RFCOMM:
+ return PROFILE_PAN;
+
+ default:
+ return PROFILE_PAN;
+ }
+}
+
+static struct rtl_hci_conn *rtl_hci_conn_lookup(struct rtl_btpf *btpf,
+ u16 handle)
+{
+ struct list_head *head = &btpf->conn_list;
+ struct list_head *p, *n;
+ struct rtl_hci_conn *conn;
+
+ list_for_each_safe(p, n, head) {
+ conn = list_entry(p, struct rtl_hci_conn, list);
+ if ((handle & 0xfff) == conn->handle)
+ return conn;
+ }
+
+ return NULL;
+}
+
+static void rtl_hci_conn_list_purge(struct rtl_btpf *btpf)
+{
+ struct list_head *head = &btpf->conn_list;
+ struct list_head *p, *n;
+ struct rtl_hci_conn *conn;
+
+ list_for_each_safe(p, n, head) {
+ conn = list_entry(p, struct rtl_hci_conn, list);
+ if (conn) {
+ list_del(&conn->list);
+ kfree(conn);
+ }
+ }
+}
+
+static struct rtl_profile *profile_alloc(u16 handle, u16 psm, u8 idx,
+ u16 dcid, u16 scid)
+{
+ struct rtl_profile *pf;
+
+ pf = kzalloc(sizeof(struct rtl_profile), GFP_KERNEL);
+ if (!pf)
+ return NULL;
+
+ pf->handle = handle;
+ pf->psm = psm;
+ pf->scid = scid;
+ pf->dcid = dcid;
+ pf->idx = idx;
+ INIT_LIST_HEAD(&pf->list);
+
+ return pf;
+}
+
+static void rtl_profile_list_purge(struct rtl_btpf *btpf)
+{
+ struct list_head *head = &btpf->pf_list;
+ struct list_head *p, *n;
+ struct rtl_profile *pf;
+
+ list_for_each_safe(p, n, head) {
+ pf = list_entry(p, struct rtl_profile, list);
+ list_del(&pf->list);
+ kfree(pf);
+ }
+}
+
+static struct rtl_profile *rtl_profile_lookup(struct rtl_btpf *btpf,
+ struct rtl_profile_id *id)
+{
+ struct list_head *head = &btpf->pf_list;
+ struct list_head *p, *n;
+ struct rtl_profile *tmp;
+ u16 handle = id->handle;
+
+ if (!id->match_flags) {
+ rtlbt_warn("%s: no match flags", __func__);
+ return NULL;
+ }
+
+ list_for_each_safe(p, n, head) {
+ tmp = list_entry(p, struct rtl_profile, list);
+
+ if ((id->match_flags & RTL_PROFILE_MATCH_HANDLE) &&
+ (handle & 0xfff) != tmp->handle)
+ continue;
+
+ if ((id->match_flags & RTL_PROFILE_MATCH_SCID) &&
+ id->scid != tmp->scid)
+ continue;
+
+ if ((id->match_flags & RTL_PROFILE_MATCH_DCID) &&
+ id->dcid != tmp->dcid)
+ continue;
+
+ return tmp;
+ }
+
+ return NULL;
+}
+
+static int hci_cmd_send_to_fw(struct rtl_btpf *btpf, u16 opcode, u8 dlen,
+ u8 *data)
+{
+ int n = 1 + 3 + dlen;
+ u8 buff[BTPF_CMD_MAXLEN];
+ struct kvec iv = { buff, n };
+ struct msghdr msg;
+ int ret;
+
+ if (!test_bit(BTPF_HCI_SOCK, &btpf->flags) ||
+ !test_bit(BTPF_CID_RTL, &btpf->flags))
+ return -1;
+
+ rtlbt_info("%s: opcode 0x%04x", __func__, opcode);
+ if (n > BTPF_CMD_MAXLEN) {
+ rtlbt_err("vendor cmd too large");
+ return -1;
+ }
+
+ buff[0] = HCI_COMMAND_PKT;
+ buff[1] = opcode & 0xff;
+ buff[2] = (opcode >> 8) & 0xff;
+ buff[3] = dlen;
+ memcpy(buff + 4, data, dlen);
+
+ memset(&msg, 0, sizeof(msg));
+
+ ret = kernel_sendmsg(btpf->hci_sock, &msg, &iv, 1, n);
+ if (ret < 0) {
+ rtlbt_err("sendmsg failed: %d", ret);
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static void btpf_update_to_controller(struct rtl_btpf *btpf)
+{
+ struct list_head *head, *pos, *q;
+ struct rtl_hci_conn *conn;
+ u8 handle_num;
+ u32 buff_sz;
+ u8 *buff;
+ u8 *p;
+
+ if (!test_bit(BTPF_CID_RTL, &btpf->flags))
+ return;
+
+ head = &btpf->conn_list;
+ handle_num = 0;
+ list_for_each_safe(pos, q, head) {
+ conn = list_entry(pos, struct rtl_hci_conn, list);
+ if (conn && conn->pf_bits)
+ handle_num++;
+ }
+
+ buff_sz = 1 + handle_num * 3 + 1;
+
+ rtlbt_info("%s: buff_sz %u, handle_num %u", __func__, buff_sz,
+ handle_num);
+
+ buff = kzalloc(buff_sz, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+ p = buff;
+ *p++ = handle_num;
+ head = &btpf->conn_list;
+ list_for_each(pos, head) {
+ conn = list_entry(pos, struct rtl_hci_conn, list);
+ if (conn && conn->pf_bits) {
+ put_unaligned_le16(conn->handle, p);
+ p += 2;
+ rtlbt_info("%s: handle 0x%04x, pf_bits 0x%02x",
+ __func__, conn->handle, conn->pf_bits);
+ *p++ = conn->pf_bits;
+ handle_num--;
+ }
+ if (!handle_num)
+ break;
+ }
+ *p++ = btpf->pf_state;
+
+ rtlbt_info("%s: pf_state 0x%02x", __func__, btpf->pf_state);
+
+ hci_cmd_send_to_fw(btpf, HCI_VENDOR_SET_PF_REPORT_CMD, buff_sz, buff);
+
+ kfree(buff);
+}
+
+static void update_profile_state(struct rtl_btpf *btpf, u8 idx, u8 busy)
+{
+ u8 update = 0;
+
+ if (!(btpf->pf_bits & BIT(idx))) {
+ rtlbt_err("%s: profile(%x) not exist", __func__, idx);
+ return;
+ }
+
+ if (busy) {
+ if (!(btpf->pf_state & BIT(idx))) {
+ update = 1;
+ btpf->pf_state |= BIT(idx);
+ }
+ } else {
+ if (btpf->pf_state & BIT(idx)) {
+ update = 1;
+ btpf->pf_state &= ~BIT(idx);
+ }
+ }
+
+ if (update) {
+ rtlbt_info("%s: pf_bits 0x%02x", __func__, btpf->pf_bits);
+ rtlbt_info("%s: pf_state 0x%02x", __func__, btpf->pf_state);
+ btpf_update_to_controller(btpf);
+ }
+}
+
+static void a2dp_do_poll(unsigned long data)
+{
+ struct rtl_btpf *btpf = (struct rtl_btpf *)data;
+
+ rtlbt_dbg("%s: icount.a2dp %d", __func__, btpf->icount.a2dp);
+
+ if (!btpf->icount.a2dp) {
+ if (btpf->pf_state & BIT(PROFILE_A2DP)) {
+ rtlbt_info("%s: a2dp state, busy to idle", __func__);
+ update_profile_state(btpf, PROFILE_A2DP, 0);
+ }
+ }
+
+ btpf->icount.a2dp = 0;
+ mod_timer(&btpf->a2dp_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+static void pan_do_poll(unsigned long data)
+{
+ struct rtl_btpf *btpf = (struct rtl_btpf *)data;
+
+ rtlbt_dbg("%s: icount.pan %d", __func__, btpf->icount.pan);
+
+ if (btpf->icount.pan < PAN_PACKET_COUNT) {
+ if (btpf->pf_state & BIT(PROFILE_PAN)) {
+ rtlbt_info("%s: pan state, busy to idle", __func__);
+ update_profile_state(btpf, PROFILE_PAN, 0);
+ }
+ } else {
+ if (!(btpf->pf_state & BIT(PROFILE_PAN))) {
+ rtlbt_info("%s: pan state, idle to busy", __func__);
+ update_profile_state(btpf, PROFILE_PAN, 1);
+ }
+ }
+
+ btpf->icount.pan = 0;
+ mod_timer(&btpf->pan_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+static void setup_monitor_timer(struct rtl_btpf *btpf, u8 idx)
+{
+ switch (idx) {
+ case PROFILE_A2DP:
+ btpf->icount.a2dp = 0;
+ setup_timer(&btpf->a2dp_timer, a2dp_do_poll,
+ (unsigned long)btpf);
+ btpf->a2dp_timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&btpf->a2dp_timer);
+ break;
+ case PROFILE_PAN:
+ btpf->icount.pan = 0;
+ setup_timer(&btpf->pan_timer, pan_do_poll, (unsigned long)btpf);
+ btpf->pan_timer.expires = jiffies + msecs_to_jiffies(1000);
+ add_timer(&(btpf->pan_timer));
+ break;
+ default:
+ break;
+ }
+}
+
+static void del_monitor_timer(struct rtl_btpf *btpf, u8 idx)
+{
+ switch (idx) {
+ case PROFILE_A2DP:
+ btpf->icount.a2dp = 0;
+ del_timer_sync(&btpf->a2dp_timer);
+ break;
+ case PROFILE_PAN:
+ btpf->icount.pan = 0;
+ del_timer_sync(&btpf->pan_timer);
+ break;
+ default:
+ break;
+ }
+}
+
+static int profile_conn_get(struct rtl_btpf *btpf, struct rtl_hci_conn *conn,
+ u8 idx)
+{
+ int update = 0;
+ u8 i;
+
+ rtlbt_dbg("%s: idx %u", __func__, idx);
+
+ if (!conn || idx >= PROFILE_MAX)
+ return -EINVAL;
+
+ if (!btpf->pf_refs[idx]) {
+ update = 1;
+ btpf->pf_bits |= BIT(idx);
+
+ /* SCO is always busy */
+ if (idx == PROFILE_SCO)
+ btpf->pf_state |= BIT(idx);
+
+ setup_monitor_timer(btpf, idx);
+ }
+ btpf->pf_refs[idx]++;
+
+ if (!conn->pf_refs[idx]) {
+ update = 1;
+ conn->pf_bits |= BIT(idx);
+ }
+ conn->pf_refs[idx]++;
+
+ rtlbt_info("%s: btpf->pf_bits 0x%02x", __func__, btpf->pf_bits);
+ for (i = 0; i < MAX_PROFILE_NUM; i++)
+ rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, i,
+ btpf->pf_refs[i]);
+
+ if (update)
+ btpf_update_to_controller(btpf);
+
+ return 0;
+}
+
+static int profile_conn_put(struct rtl_btpf *btpf, struct rtl_hci_conn *conn,
+ u8 idx)
+{
+ int need_update = 0;
+ u8 i;
+
+ rtlbt_dbg("%s: idx %u", __func__, idx);
+
+ if (!conn || idx >= PROFILE_MAX)
+ return -EINVAL;
+
+ btpf->pf_refs[idx]--;
+ if (!btpf->pf_refs[idx]) {
+ need_update = 1;
+ btpf->pf_bits &= ~BIT(idx);
+ btpf->pf_state &= ~BIT(idx);
+ del_monitor_timer(btpf, idx);
+ }
+
+ conn->pf_refs[idx]--;
+ if (!conn->pf_refs[idx]) {
+ need_update = 1;
+ conn->pf_bits &= ~BIT(idx);
+
+ /* Clear hid interval if needed */
+ if (idx == PROFILE_HID &&
+ (conn->pf_bits & BIT(PROFILE_HID2))) {
+ conn->pf_bits &= ~BIT(PROFILE_HID2);
+ btpf->pf_refs[PROFILE_HID2]--;
+ }
+ }
+
+ rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, idx,
+ btpf->pf_refs[idx]);
+ rtlbt_info("%s: pf_bits 0x%02x", __func__, btpf->pf_bits);
+ for (i = 0; i < MAX_PROFILE_NUM; i++)
+ rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, i,
+ btpf->pf_refs[i]);
+
+ if (need_update)
+ btpf_update_to_controller(btpf);
+
+ return 0;
+}
+
+static void hid_state_update(struct rtl_btpf *btpf, u16 handle,
+ u16 interval)
+{
+ u8 update = 0;
+ struct rtl_hci_conn *conn;
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn)
+ return;
+
+ rtlbt_info("%s: handle 0x%04x, interval 0x%x", __func__, handle,
+ interval);
+ if (!(conn->pf_bits & BIT(PROFILE_HID))) {
+ rtlbt_dbg("hid not connected in the handle");
+ return;
+ }
+
+ if (interval < 60) {
+ if (!(conn->pf_bits & BIT(PROFILE_HID2))) {
+ update = 1;
+ conn->pf_bits |= BIT(PROFILE_HID2);
+
+ btpf->pf_refs[PROFILE_HID2]++;
+ if (btpf->pf_refs[PROFILE_HID2] == 1)
+ btpf->pf_state |= BIT(PROFILE_HID);
+ }
+ } else {
+ if (conn->pf_bits & BIT(PROFILE_HID2)) {
+ update = 1;
+ conn->pf_bits &= ~BIT(PROFILE_HID2);
+
+ btpf->pf_refs[PROFILE_HID2]--;
+ if (!btpf->pf_refs[PROFILE_HID2])
+ btpf->pf_state &= ~BIT(PROFILE_HID);
+ }
+ }
+
+ if (update)
+ btpf_update_to_controller(btpf);
+}
+
+static int handle_l2cap_conn_req(struct rtl_btpf *btpf, u16 handle, u16 psm,
+ u16 cid, u8 dir)
+{
+ struct rtl_profile *pf;
+ int idx = psm_to_profile(psm);
+ struct rtl_profile_id id;
+
+ if (idx < 0) {
+ rtlbt_info("no need to parse psm %04x", psm);
+ return 0;
+ }
+
+ memset(&id, 0, sizeof(id));
+ id.match_flags = RTL_PROFILE_MATCH_HANDLE;
+ id.handle = handle;
+
+ if (dir == RTL_TO_REMOTE) {
+ id.match_flags |= RTL_PROFILE_MATCH_SCID;
+ id.scid = cid;
+ } else {
+ id.match_flags |= RTL_PROFILE_MATCH_DCID;
+ id.dcid = cid;
+ }
+
+ pf = rtl_profile_lookup(btpf, &id);
+
+ if (pf) {
+ rtlbt_warn("%s: profile already exists", __func__);
+ return -1;
+ }
+
+ if (dir == RTL_TO_REMOTE)
+ pf = profile_alloc(handle, psm, (u8)idx, 0, cid);
+ else
+ pf = profile_alloc(handle, psm, (u8)idx, cid, 0);
+
+ if (!pf) {
+ rtlbt_err("%s: allocate profile failed", __func__);
+ return -1;
+ }
+
+ list_add_tail(&pf->list, &btpf->pf_list);
+
+ return 0;
+}
+
+/* dcid is the cid on the device sending this resp packet.
+ * scid is the cid on the device receiving the resp packet.
+ */
+static u8 handle_l2cap_conn_rsp(struct rtl_btpf *btpf,
+ u16 handle, u16 dcid,
+ u16 scid, u8 dir, u8 result)
+{
+ struct rtl_profile *pf;
+ struct rtl_hci_conn *conn;
+ struct rtl_profile_id id = {
+ .match_flags = RTL_PROFILE_MATCH_HANDLE,
+ .handle = handle,
+ };
+
+ if (dir == RTL_FROM_REMOTE) {
+ id.match_flags |= RTL_PROFILE_MATCH_SCID;
+ id.scid = scid;
+ pf = rtl_profile_lookup(btpf, &id);
+ } else {
+ id.match_flags |= RTL_PROFILE_MATCH_DCID;
+ id.dcid = scid;
+ pf = rtl_profile_lookup(btpf, &id);
+ }
+
+ if (!pf) {
+ rtlbt_err("%s: profile not found", __func__);
+ return -1;
+ }
+
+ if (!result) {
+ rtlbt_info("l2cap connection success");
+ if (dir == RTL_FROM_REMOTE)
+ pf->dcid = dcid;
+ else
+ pf->scid = dcid;
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (conn)
+ profile_conn_get(btpf, conn, pf->idx);
+ }
+
+ return 0;
+}
+
+static int handle_l2cap_disconn_req(struct rtl_btpf *btpf,
+ u16 handle, u16 dcid,
+ u16 scid, u8 dir)
+{
+ struct rtl_profile *pf;
+ struct rtl_hci_conn *conn;
+ int err = 0;
+ struct rtl_profile_id id = {
+ .match_flags = RTL_PROFILE_MATCH_HANDLE |
+ RTL_PROFILE_MATCH_SCID |
+ RTL_PROFILE_MATCH_DCID,
+ .handle = handle,
+ .scid = scid,
+ .dcid = dcid,
+ };
+
+ if (dir == RTL_FROM_REMOTE) {
+ id.scid = dcid;
+ id.dcid = scid;
+ pf = rtl_profile_lookup(btpf, &id);
+ } else {
+ pf = rtl_profile_lookup(btpf, &id);
+ }
+
+ if (!pf) {
+ rtlbt_err("%s: no profile", __func__);
+ err = -1;
+ goto done;
+ }
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn) {
+ rtlbt_err("%s: no connection", __func__);
+ err = -1;
+ goto done;
+ }
+
+ profile_conn_put(btpf, conn, pf->idx);
+ list_del(&pf->list);
+ kfree(pf);
+
+done:
+ rtlbt_info("%s: handle %04x, dcid %04x, scid %04x, dir %x",
+ __func__, handle, dcid, scid, dir);
+
+ return 0;
+}
+
+static const char sample_freqs[4][8] = {
+ "16", "32", "44.1", "48"
+};
+
+static const u8 sbc_blocks[4] = { 4, 8, 12, 16 };
+
+static const char chan_modes[4][16] = {
+ "MONO", "DUAL_CHANNEL", "STEREO", "JOINT_STEREO"
+};
+
+static const char alloc_methods[2][12] = {
+ "LOUDNESS", "SNR"
+};
+
+static const u8 subbands[2] = { 4, 8 };
+
+static void pr_sbc_hdr(struct sbc_frame_hdr *hdr)
+{
+ rtlbt_info("syncword: %02x", hdr->syncword);
+ rtlbt_info("freq %skHz", sample_freqs[hdr->sampling_frequency]);
+ rtlbt_info("blocks %u", sbc_blocks[hdr->blocks]);
+ rtlbt_info("channel mode %s", chan_modes[hdr->channel_mode]);
+ rtlbt_info("allocation method %s",
+ alloc_methods[hdr->allocation_method]);
+ rtlbt_info("subbands %u", subbands[hdr->subbands]);
+}
+
+static void packet_increment(struct rtl_btpf *btpf, u16 handle,
+ u16 ch_id, u16 length, u8 *payload, u8 dir)
+{
+ struct rtl_profile *pf;
+ struct rtl_hci_conn *conn;
+ struct rtl_profile_id id;
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn)
+ goto done;
+
+ if (conn->type != ACL_CONN)
+ return;
+
+ memset(&id, 0, sizeof(id));
+ id.match_flags = RTL_PROFILE_MATCH_HANDLE;
+ id.handle = handle;
+ if (dir == RTL_FROM_REMOTE) {
+ id.match_flags |= RTL_PROFILE_MATCH_SCID;
+ id.scid = ch_id;
+ } else {
+ id.match_flags |= RTL_PROFILE_MATCH_DCID;
+ id.dcid = ch_id;
+ }
+ pf = rtl_profile_lookup(btpf, &id);
+ if (!pf)
+ goto done;
+
+ if (pf->idx == PROFILE_A2DP && length > 100) {
+ /* avdtp media data */
+ if (!(btpf->pf_state & BIT(PROFILE_A2DP))) {
+ struct sbc_frame_hdr *sbc_hdr;
+ struct rtp_header *rtp_hdr;
+ u8 bitpool;
+
+ update_profile_state(btpf, PROFILE_A2DP, 1);
+ rtp_hdr = (struct rtp_header *)payload;
+
+ rtlbt_info("rtp: v %u, cc %u, pt %u", rtp_hdr->v,
+ rtp_hdr->cc, rtp_hdr->pt);
+
+ payload += sizeof(*rtp_hdr) + rtp_hdr->cc * 4 + 1;
+
+ sbc_hdr = (struct sbc_frame_hdr *)payload;
+
+ rtlbt_info("bitpool %u", sbc_hdr->bitpool);
+
+ pr_sbc_hdr(sbc_hdr);
+
+ bitpool = sbc_hdr->bitpool;
+ hci_cmd_send_to_fw(btpf, HCI_VENDOR_SET_BITPOOL_CMD, 1,
+ &bitpool);
+ }
+ btpf->icount.a2dp++;
+
+ }
+
+ if (pf->idx == PROFILE_PAN)
+ btpf->icount.pan++;
+
+done:
+ return;
+}
+
+static void hci_cmd_complete_evt(struct rtl_btpf *btpf, u8 total_len, u8 *p)
+{
+ u16 opcode;
+ struct hci_ev_cmd_complete *cmdcp;
+
+ cmdcp = (struct hci_ev_cmd_complete *)p;
+ opcode = le16_to_cpu(cmdcp->opcode);
+
+ switch (opcode) {
+ case HCI_OP_READ_LOCAL_VERSION: {
+ struct hci_rp_read_local_version *v =
+ (struct hci_rp_read_local_version *)(p +
+ sizeof(*cmdcp));
+ if (v->status)
+ break;
+
+ btpf->hci_rev = le16_to_cpu(v->hci_rev);
+ btpf->lmp_subver = le16_to_cpu(v->lmp_subver);
+ rtlbt_info("HCI Rev 0x%04x, LMP Subver 0x%04x", btpf->hci_rev,
+ btpf->lmp_subver);
+
+ if (le16_to_cpu(v->manufacturer) == 0x005d) {
+ rtlbt_info("Realtek Semiconductor Corporation");
+ set_bit(BTPF_CID_RTL, &btpf->flags);
+ } else {
+ clear_bit(BTPF_CID_RTL, &btpf->flags);
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void hci_conn_complete_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_conn_complete *ev = (void *)p;
+ u16 handle;
+ struct rtl_hci_conn *conn;
+
+ handle = __le16_to_cpu(ev->handle);
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn) {
+ conn = kzalloc(sizeof(struct rtl_hci_conn), GFP_KERNEL);
+ if (conn) {
+ conn->handle = handle;
+ list_add_tail(&conn->list, &btpf->conn_list);
+ conn->pf_bits = 0;
+ memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
+ /* sco or esco */
+ if (ev->link_type == 0 || ev->link_type == 2) {
+ conn->type = SYNC_CONN;
+ profile_conn_get(btpf, conn, PROFILE_SCO);
+ } else {
+ conn->type = ACL_CONN;
+ }
+ } else {
+ rtlbt_err("%s: hci conn allocate fail.", __func__);
+ return;
+ }
+ } else {
+ /* If the connection has already existed, reset connection
+ * information
+ */
+ rtlbt_warn("%s: hci conn handle(0x%x) already existed",
+ __func__, handle);
+ conn->pf_bits = 0;
+ memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
+ /* sco or esco */
+ if (ev->link_type == 0 || ev->link_type == 2) {
+ conn->type = SYNC_CONN;
+ profile_conn_get(btpf, conn, PROFILE_SCO);
+ } else {
+ conn->type = ACL_CONN;
+ }
+ }
+}
+
+static int hci_disconn_complete_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_disconn_complete *ev = (void *)p;
+ u16 handle;
+ struct rtl_hci_conn *conn;
+ struct list_head *pos, *temp;
+ struct rtl_profile *pf;
+
+ handle = le16_to_cpu(ev->handle);
+
+ rtlbt_info("%s: status %u, handle %04x, reason 0x%x", __func__,
+ ev->status, handle, ev->reason);
+
+ if (ev->status)
+ return -1;
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn) {
+ rtlbt_err("hci conn handle(0x%x) not found", handle);
+ return -1;
+ }
+
+ switch (conn->type) {
+ case ACL_CONN:
+ list_for_each_safe(pos, temp, &btpf->pf_list) {
+ pf = list_entry(pos, struct rtl_profile, list);
+ if (pf->handle == handle && pf->scid && pf->dcid) {
+ rtlbt_info(
+ "%s: hndl %04x psm %04x dcid %04x scid %04x",
+ __func__, pf->handle, pf->psm, pf->dcid,
+ pf->scid);
+ /* If both scid and dcid are bigger than zero,
+ * L2cap connection exists.
+ */
+ profile_conn_put(btpf, conn, pf->idx);
+ list_del(&pf->list);
+ kfree(pf);
+ }
+ }
+ break;
+
+ case SYNC_CONN:
+ profile_conn_put(btpf, conn, PROFILE_SCO);
+ break;
+
+ case LE_CONN:
+ profile_conn_put(btpf, conn, PROFILE_HID);
+ break;
+
+ default:
+ break;
+ }
+
+ list_del(&conn->list);
+ kfree(conn);
+
+ return 0;
+}
+
+static void hci_mode_change_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_mode_change *ev = (void *)p;
+
+ hid_state_update(btpf, le16_to_cpu(ev->handle),
+ le16_to_cpu(ev->interval));
+}
+
+static void rtl_le_conn_compl_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_le_conn_complete *ev = (void *)p;
+ u16 handle, interval;
+ struct rtl_hci_conn *conn;
+
+ handle = le16_to_cpu(ev->handle);
+ interval = le16_to_cpu(ev->interval);
+
+ conn = rtl_hci_conn_lookup(btpf, handle);
+ if (!conn) {
+ conn = kzalloc(sizeof(struct rtl_hci_conn), GFP_ATOMIC);
+ if (conn) {
+ conn->handle = handle;
+ list_add_tail(&conn->list, &btpf->conn_list);
+ conn->pf_bits = 0;
+ memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
+ conn->type = LE_CONN;
+ /* We consider le is the same as hid */
+ profile_conn_get(btpf, conn, PROFILE_HID);
+ hid_state_update(btpf, handle, interval);
+ } else {
+ rtlbt_err("%s: hci conn allocate fail.", __func__);
+ }
+ } else {
+ rtlbt_warn("%s: hci conn handle(%x) already existed.", __func__,
+ handle);
+ conn->pf_bits = 0;
+ memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
+ conn->type = LE_CONN;
+ profile_conn_get(btpf, conn, PROFILE_HID);
+ hid_state_update(btpf, handle, interval);
+ }
+}
+
+static void hci_le_conn_complete_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_le_conn_update_complete *ev = (void *)p;
+ u16 handle, interval;
+
+ handle = le16_to_cpu(ev->handle);
+ interval = le16_to_cpu(ev->interval);
+ hid_state_update(btpf, handle, interval);
+}
+
+static void hci_le_meta_evt(struct rtl_btpf *btpf, u8 *p)
+{
+ struct hci_ev_le_meta *le_ev = (void *)p;
+
+ p += sizeof(struct hci_ev_le_meta);
+
+ switch (le_ev->subevent) {
+ case HCI_EV_LE_CONN_COMPLETE:
+ rtl_le_conn_compl_evt(btpf, p);
+ break;
+
+ case HCI_EV_LE_CONN_UPDATE_COMPLETE:
+ hci_le_conn_complete_evt(btpf, p);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void hci_process_evt(struct rtl_btpf *btpf, u8 *p, u16 len)
+{
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *)p;
+
+ (void)&len;
+
+ p += sizeof(struct hci_event_hdr);
+
+ switch (hdr->evt) {
+ case HCI_EV_CMD_COMPLETE:
+ hci_cmd_complete_evt(btpf, hdr->plen, p);
+ break;
+ case HCI_EV_CONN_COMPLETE:
+ case HCI_EV_SYNC_CONN_COMPLETE:
+ hci_conn_complete_evt(btpf, p);
+ break;
+ case HCI_EV_DISCONN_COMPLETE:
+ hci_disconn_complete_evt(btpf, p);
+ break;
+ case HCI_EV_MODE_CHANGE:
+ hci_mode_change_evt(btpf, p);
+ break;
+ case HCI_EV_LE_META:
+ hci_le_meta_evt(btpf, p);
+ break;
+ default:
+ break;
+ }
+}
+
+static const char l2_dir_str[][4] = {
+ "RX", "TX",
+};
+
+static void l2_process_frame(struct rtl_btpf *btpf, u8 *data, u16 len,
+ u8 out)
+{
+ u16 handle;
+ u16 flags;
+ u16 chann_id;
+ u16 psm, scid, dcid, result;
+ struct hci_acl_hdr *acl_hdr = (void *)data;
+ struct l2cap_cmd_hdr *cmd;
+ struct l2cap_hdr *hdr;
+ struct l2cap_conn_req *conn_req;
+ struct l2cap_conn_rsp *conn_rsp;
+ struct l2cap_disconn_req *disc_req;
+
+ handle = __le16_to_cpu(acl_hdr->handle);
+ flags = hci_flags(handle);
+ handle = hci_handle(handle);
+
+ if (flags == ACL_CONT)
+ return;
+
+ data += sizeof(*acl_hdr);
+
+ hdr = (void *)data;
+ chann_id = le16_to_cpu(hdr->cid);
+
+ if (chann_id != 0x0001) {
+ if (btpf->pf_bits & BIT(PROFILE_A2DP) ||
+ btpf->pf_bits & BIT(PROFILE_PAN))
+ packet_increment(btpf, handle, chann_id,
+ le16_to_cpu(hdr->len), data + 4, out);
+ return;
+ }
+
+ data += sizeof(*hdr);
+
+ cmd = (void *)data;
+ data += sizeof(*cmd);
+
+ switch (cmd->code) {
+ case L2CAP_CONN_REQ:
+ conn_req = (void *)data;
+ psm = le16_to_cpu(conn_req->psm);
+ scid = le16_to_cpu(conn_req->scid);
+ rtlbt_info(
+ "%s l2cap conn req: hndl %04x psm %04x scid %04x",
+ l2_dir_str[out], handle, psm, scid);
+ handle_l2cap_conn_req(btpf, handle, psm, scid, out);
+ break;
+
+ case L2CAP_CONN_RSP:
+ conn_rsp = (void *)data;
+ dcid = le16_to_cpu(conn_rsp->dcid);
+ scid = le16_to_cpu(conn_rsp->scid);
+ result = le16_to_cpu(conn_rsp->result);
+ rtlbt_info(
+ "%s l2cap conn rsp: hndl %04x dcid %04x scid %04x res %x",
+ l2_dir_str[out], handle, dcid, scid, result);
+ handle_l2cap_conn_rsp(btpf, handle, dcid, scid, out, result);
+ break;
+
+ case L2CAP_DISCONN_REQ:
+ disc_req = (void *)data;
+ dcid = le16_to_cpu(disc_req->dcid);
+ scid = le16_to_cpu(disc_req->scid);
+ rtlbt_info(
+ "%s l2cap disc req: hndl %04x dcid %04x scid %04x",
+ l2_dir_str[out], handle, dcid, scid);
+ handle_l2cap_disconn_req(btpf, handle, dcid, scid, out);
+ break;
+ case L2CAP_DISCONN_RSP:
+ break;
+ default:
+ rtlbt_dbg("undesired l2 command code 0x%02x", cmd->code);
+ break;
+ }
+}
+
+static void btpf_process_frame(struct rtl_btpf *btpf, struct sk_buff *skb)
+{
+ u8 pkt_type = skb->data[0];
+
+ skb_pull(skb, 1);
+
+ if (!test_bit(BTPF_CID_RTL, &btpf->flags)) {
+ if (pkt_type == HCI_EVENT_PKT) {
+ struct hci_event_hdr *hdr = (void *)skb->data;
+
+ if (hdr->evt == HCI_EV_CMD_COMPLETE) {
+ skb_pull(skb, sizeof(*hdr));
+ hci_cmd_complete_evt(btpf, hdr->plen,
+ skb->data);
+ }
+ }
+ return;
+ }
+
+ switch (pkt_type) {
+ case HCI_EVENT_PKT:
+ hci_process_evt(btpf, skb->data, skb->len);
+ break;
+ case HCI_ACLDATA_PKT:
+ if (bt_cb(skb)->incoming)
+ l2_process_frame(btpf, skb->data, skb->len, 0);
+ else
+ l2_process_frame(btpf, skb->data, skb->len, 1);
+ break;
+ default:
+ break;
+ }
+}
+
+static void btpf_process_work(struct work_struct *work)
+{
+ struct rtl_btpf *btpf;
+ struct sock *sk;
+ struct sk_buff *skb;
+
+ btpf = container_of(work, struct rtl_btpf, hci_work);
+ sk = btpf->hci_sock->sk;
+
+ /* Get data directly from socket receive queue without copying it. */
+ while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
+ skb_orphan(skb);
+ btpf_process_frame(btpf, skb);
+ kfree_skb(skb);
+ }
+}
+
+static void btpf_raw_data_ready(struct sock *sk)
+{
+ struct rtl_btpf *btpf;
+
+ /* rtlbt_dbg("qlen %d", skb_queue_len(&sk->sk_receive_queue)); */
+
+ btpf = sk->sk_user_data;
+ queue_work(btpf->workq, &btpf->hci_work);
+}
+
+static void btpf_raw_error_report(struct sock *sk)
+{
+}
+
+static int btpf_open_socket(struct rtl_btpf *btpf)
+{
+ int ret;
+ struct sockaddr_hci addr;
+ struct sock *sk;
+ struct hci_filter flt;
+
+ ret = sock_create_kern(&init_net, PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI,
+ &btpf->hci_sock);
+ if (ret < 0) {
+ rtlbt_err("Create hci sock error %d", ret);
+ goto err_1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_family = AF_BLUETOOTH;
+ /* Assume Realtek BT controller index is 0. */
+ addr.hci_dev = 0;
+ addr.hci_channel = HCI_CHANNEL_RAW;
+ ret = kernel_bind(btpf->hci_sock, (struct sockaddr *)&addr,
+ sizeof(addr));
+ if (ret < 0) {
+ rtlbt_err("Bind hci sock error");
+ goto err_2;
+ }
+
+ memset(&flt, 0, sizeof(flt));
+ /* flt.type_mask = 0; */
+ flt.type_mask = (1 << HCI_EVENT_PKT | 1 << HCI_ACLDATA_PKT);
+ flt.event_mask[0] = 0xffffffff;
+ flt.event_mask[1] = 0xffffffff;
+
+ ret = kernel_setsockopt(btpf->hci_sock, SOL_HCI, HCI_FILTER,
+ (char *)&flt, sizeof(flt));
+ if (ret < 0) {
+ rtlbt_err("Set hci sock filter error %d", ret);
+ goto err_2;
+ }
+
+ sk = btpf->hci_sock->sk;
+ sk->sk_user_data = btpf;
+ sk->sk_data_ready = btpf_raw_data_ready;
+ sk->sk_error_report = btpf_raw_error_report;
+
+ set_bit(BTPF_HCI_SOCK, &btpf->flags);
+
+ return 0;
+err_2:
+ sock_release(btpf->hci_sock);
+err_1:
+ return ret;
+}
+
+static void btpf_close_socket(struct rtl_btpf *btpf)
+{
+ struct socket *socket = btpf->hci_sock;
+
+ if (socket) {
+ btpf->hci_sock = NULL;
+ kernel_sock_shutdown(socket, SHUT_RDWR);
+ socket->sk->sk_user_data = NULL;
+ sock_release(socket);
+ }
+
+ clear_bit(BTPF_HCI_SOCK, &btpf->flags);
+}
+
+int rtl_btpf_init(void)
+{
+ int i;
+ struct rtl_btpf *btpf;
+ int ret = 0;
+
+ btpf = kzalloc(sizeof(struct rtl_btpf), GFP_KERNEL);
+ if (!btpf)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&btpf->conn_list);
+ INIT_LIST_HEAD(&btpf->pf_list);
+
+ btpf->pf_bits = 0;
+ btpf->pf_state = 0;
+ for (i = 0; i < MAX_PROFILE_NUM; i++)
+ btpf->pf_refs[i] = 0;
+
+ INIT_WORK(&btpf->hci_work, btpf_process_work);
+
+ btpf->workq = create_workqueue("rtl_btpf_workq");
+ if (!btpf->workq) {
+ ret = -ENOMEM;
+ goto err_1;
+ }
+
+ /* init sock */
+ ret = btpf_open_socket(btpf);
+ if (ret < 0) {
+ rtlbt_err("Failed to open sock to monitor tx/rx");
+ goto err_2;
+ }
+
+ rtl_btpf = btpf;
+
+ rtlbt_info("rtl btpf initialized");
+
+ return 0;
+err_2:
+ flush_workqueue(btpf->workq);
+ destroy_workqueue(btpf->workq);
+err_1:
+ kfree(btpf);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rtl_btpf_init);
+
+void rtl_btpf_deinit(void)
+{
+ struct rtl_btpf *btpf = rtl_btpf;
+
+ rtlbt_info("rtl btpf de-initialize");
+
+ rtl_btpf = NULL;
+
+ if (!btpf)
+ return;
+
+ flush_workqueue(btpf->workq);
+ destroy_workqueue(btpf->workq);
+
+ del_timer_sync(&btpf->a2dp_timer);
+ del_timer_sync(&btpf->pan_timer);
+
+ rtl_hci_conn_list_purge(btpf);
+ rtl_profile_list_purge(btpf);
+
+ btpf_close_socket(btpf);
+
+ kfree(btpf);
+}
+EXPORT_SYMBOL_GPL(rtl_btpf_deinit);
+
+MODULE_AUTHOR("Alex Lu <[email protected]>");
+MODULE_DESCRIPTION("Bluetooth profiling for Realtek devices ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/rtl_btpf.h b/drivers/bluetooth/rtl_btpf.h
new file mode 100644
index 0000000..2d507b0
--- /dev/null
+++ b/drivers/bluetooth/rtl_btpf.h
@@ -0,0 +1,184 @@
+/*
+ *
+ * Realtek Bluetooth Profile profiling driver
+ *
+ * Copyright (C) 2015 Realtek Semiconductor Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <net/bluetooth/hci_core.h>
+#include <linux/list.h>
+
+#define rtlbt_dbg(fmt, ...) \
+ pr_debug("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
+#define rtlbt_info(fmt, ...) \
+ pr_info("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
+#define rtlbt_warn(fmt, ...) \
+ pr_warn("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
+#define rtlbt_err(fmt, ...) \
+ pr_err("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
+
+#define HCI_VENDOR_SET_PF_REPORT_CMD 0xfc19
+#define HCI_VENDOR_SET_BITPOOL_CMD 0xfc51
+
+#define PAN_PACKET_COUNT 5
+
+#define ACL_CONN 0x0
+#define SYNC_CONN 0x1
+#define LE_CONN 0x2
+
+#define PSM_SDP 0x0001
+#define PSM_RFCOMM 0x0003
+#define PSM_PAN 0x000F
+#define PSM_HID 0x0011
+#define PSM_HID_INT 0x0013
+#define PSM_AVCTP 0x0017
+#define PSM_AVDTP 0x0019
+#define PSM_FTP 0x1001
+#define PSM_BIP 0x1003
+#define PSM_OPP 0x1015
+
+#define MAX_PROFILE_NUM 7
+enum __profile_type {
+ PROFILE_SCO = 0,
+ PROFILE_HID = 1,
+ PROFILE_A2DP = 2,
+ PROFILE_PAN = 3,
+ PROFILE_HID2 = 4, /* hid interval */
+ PROFILE_HOGP = 5,
+ PROFILE_VOICE = 6,
+ PROFILE_MAX = 7
+};
+
+struct pf_pkt_icount {
+ u32 a2dp;
+ u32 pan;
+ u32 hogp;
+ u32 voice;
+};
+
+#define RTL_FROM_REMOTE 0
+#define RTL_TO_REMOTE 1
+
+#define RTL_PROFILE_MATCH_HANDLE (1 << 0)
+#define RTL_PROFILE_MATCH_SCID (1 << 1)
+#define RTL_PROFILE_MATCH_DCID (1 << 2)
+struct rtl_profile_id {
+ u16 match_flags;
+ u16 handle;
+ u16 dcid;
+ u16 scid;
+};
+
+struct rtl_profile {
+ struct list_head list;
+ u16 handle;
+ u16 psm;
+ u16 dcid;
+ u16 scid;
+ u8 idx;
+};
+
+struct rtl_hci_conn {
+ struct list_head list;
+ u16 handle;
+ u8 type;
+ u8 pf_bits;
+ int pf_refs[MAX_PROFILE_NUM];
+};
+
+struct rtl_btpf {
+ u16 hci_rev;
+ u16 lmp_subver;
+
+ struct hci_dev *hdev;
+ struct list_head pf_list;
+ struct list_head conn_list;
+
+ u8 pf_bits;
+ u8 pf_state;
+ int pf_refs[MAX_PROFILE_NUM];
+
+ struct pf_pkt_icount icount;
+
+ /* Monitor timers */
+ struct timer_list a2dp_timer;
+ struct timer_list pan_timer;
+
+ struct workqueue_struct *workq;
+ struct work_struct hci_work;
+
+ struct socket *hci_sock;
+#define BTPF_HCI_SOCK 1
+#define BTPF_CID_RTL 2
+ unsigned long flags;
+};
+
+#ifdef __LITTLE_ENDIAN
+struct sbc_frame_hdr {
+ u8 syncword:8; /* Sync word */
+ u8 subbands:1; /* Subbands */
+ u8 allocation_method:1; /* Allocation method */
+ u8 channel_mode:2; /* Channel mode */
+ u8 blocks:2; /* Blocks */
+ u8 sampling_frequency:2; /* Sampling frequency */
+ u8 bitpool:8; /* Bitpool */
+ u8 crc_check:8; /* CRC check */
+} __packed;
+
+struct rtp_header {
+ unsigned cc:4;
+ unsigned x:1;
+ unsigned p:1;
+ unsigned v:2;
+
+ unsigned pt:7;
+ unsigned m:1;
+
+ u16 sequence_number;
+ u32 timestamp;
+ u32 ssrc;
+ u32 csrc[0];
+} __packed;
+
+#else /* !__LITTLE_ENDIAN */
+struct sbc_frame_hdr {
+ u8 syncword:8; /* Sync word */
+ u8 sampling_frequency:2; /* Sampling frequency */
+ u8 blocks:2; /* Blocks */
+ u8 channel_mode:2; /* Channel mode */
+ u8 allocation_method:1; /* Allocation method */
+ u8 subbands:1; /* Subbands */
+ u8 bitpool:8; /* Bitpool */
+ u8 crc_check:8; /* CRC check */
+} __packed;
+
+struct rtp_header {
+ unsigned v:2;
+ unsigned p:1;
+ unsigned x:1;
+ unsigned cc:4;
+
+ unsigned m:1;
+ unsigned pt:7;
+
+ u16 sequence_number;
+ u32 timestamp;
+ u32 ssrc;
+ u32 csrc[0];
+} __packed;
+#endif /* __LITTLE_ENDIAN */
+
+void rtl_btpf_deinit(void);
+int rtl_btpf_init(void);
--
2.10.2
SGkgTWFyY2VsLA0KVGhhbmtzIGZvciB5b3VyIGV4cGxhbmF0aW9uLg0KSSdtIHNvcnJ5LCBJIG11
c3QgY29ycmVjdCBvbmUgbXkgbWlzdGFrZS4NClRoZSBmaXJzdCB0eXBpY2FsIHNjZW5hcmlvIHNo
b3VsZCBiZSBwcm9maWxlIGRhdGEgdHJhbnNtaXNzaW9uIChhMmRwIG9yIG9wcC4uLikgYW5kIGlu
cXVpcnkvcGFnZSBhdCB0aGUgc2FtZSB0aW1lLiBVc2VyIG1pZ2h0IHdhbnQgdG8gY29ubmVjdCB0
byBvdGhlciBCVCBkZXZpY2Ugd2hlbiBoZS9zaGUgaXMgbGlzdGVuaW5nIG11c2ljIHdpdGggQlQg
aGVhZHNldC4NCklucXVpcnkvcGFnZSB3aWxsIG9jY3VweSBzb21lIGNvbnRpbnVvdXMgdGltZSBz
bG90cyBmb3IgdHJhbnNmZXJyaW5nIElEIHBhY2tldHMsIHdoaWNoIGFyZSBub3QgZnJvbSBob3N0
IHN0YWNrLg0KSWYgdGhlcmUgYXJlIHNvbWUgYTJkcCBtZWRpYSBwYWNrZXRzIHRvIHRyYW5zZmVy
IGFuZCBpbnF1aXJ5L3BhZ2UgcHJpb3JpdHkgaXMgaGlnaGVyIHRoYW4gdHJhbnNmZXIgQUNMIHBh
Y2tldHMsIHRoZSBBQ0wgcGFja2V0cyB0cmFuc21pc3Npb24gd291bGQgYmUgZGVsYXllZC4gQXQg
dGhpcyB0aW1lLCBhdWRpbyBleHBlcmllbmNlIHdvdWxkIG5vdCBiZSBnb29kLg0KQnV0IGlmIHRo
ZSBwYWNrZXRzIGFyZSBmcm9tIG9wcCB0cmFuc21pc3Npb24sIHRoZSBpbXBhY3Qgd291bGQgYmUg
bGVzcy4NCg0KVGhhbmtzLA0KQlJzLA0KQWxleCBMdS4NCg0KLS0tLS3Tyrz+1K28/i0tLS0tDQq3
orz+yMs6IE1hcmNlbCBIb2x0bWFubiBbbWFpbHRvOm1hcmNlbEBob2x0bWFubi5vcmddIA0Kt6LL
zcqxvOQ6IDIwMTfE6jLUwjExyNUgMTY6NDUNCsrVvP7Iyzogwr3W7M6wDQqzrcvNOiBMYXJyeSBG
aW5nZXI7IEd1c3Rhdm8gRi4gUGFkb3ZhbjsgSm9oYW4gSGVkYmVyZzsgbGludXgtYmx1ZXRvb3Ro
OyBsaW51eC1rZXJuZWxAdmdlci5rZXJuZWwub3JnDQrW98ziOiBSZTogW1BBVENIXSBydGxidDog
QWRkIFJlYWx0ZWsgQmx1ZXRvb3RoIHByb2ZpbGluZyBzdXBwb3J0DQoNCkhpIEFsZXgsDQoNCj4g
Rmlyc3QsIGxldCBtZSBleHBsYWluIHRoZSBCVCBwcm9maWxpbmcuDQo+IEJUIHByb2ZpbGluZyBw
YXJzZXMgc29tZSBwYWNrZXRzIGxpa2UgY29ubmVjdGlvbiBjb21wbGV0ZSBldmVudCwgbDJjYXAg
Y29ubiByZXEvcnNwLCAuLi4gdG8gZ2V0IHByb2ZpbGVzIGluZm9ybWF0aW9uIGZvciBmaXJtd2Fy
ZS4NCj4gRmlybXdhcmUgdXNlcyB0aGUgaW5mb3JtYXRpb24gdG8gYmFsYW5jZSBwYWNrZXQgdHJh
bnNtaXNzaW9uIHByaW9yaXR5IHRvIGdldCBnb29kIHVzZXIgZXhwZXJpZW5jZS4NCj4gVGhlIHR5
cGljYWwgc2NlbmFyaW9zOg0KPiAxLiBhMmRwIHRyYW5zbWlzc2lvbiBhbmQgb3BwIHRyYW5zbWlz
c2lvbiBhdCB0aGUgc2FtZSB0aW1lLg0KDQp0aGF0IHNob3VsZCB3b3JrIGp1c3QgZmluZSB3aXRo
IFNPX1BSSU9SSVRZLiBUaGVyZSBpcyBubyBuZWVkIGZvciB0aGUgZmlybXdhcmUgdG8gcGxheSB0
cmlja3MuIEp1c3QgdHJ1c3QgdGhhdCB3ZSBwcmlvcml0aXNlIHRoZSBwYWNrZXRzIGNvcnJlY3Rs
eS4gV2hpY2ggd2UgYWxyZWFkeSBkby4NCg0KPiAyLiBhMmRwIHRyYW5zbWlzc2lvbiBhbmQgV2lG
aSBkYXRhIHRyYW5zbWlzc2lvbiB3aXRoIG9uZSBzaGFyZWQgYW50ZW5uYSBhdCB0aGUgc2FtZSB0
aW1lLg0KPiBhMmRwIHRyYW5zbWlzc2lvbiBuZWVkcyBoaWdoZXIgcHJpb3JpdHkuDQoNCkkgd29u
ZGVyIHdoeSBub3QgdXNlIHRoZSBTT19QUklPUklUWSBzZXR0aW5nIHdlIGhhdmUgYW55d2F5LiBJ
dCBpcyBhY3R1YWxseSBtb3JlIHByZWNpc2UgdGhhbiB3aGF0IHlvdSBoYXZlIGJhc2VkIG9uIEFD
TCBoYW5kbGVzLiBTaW5jZSBpdCB3aWxsIGV2ZW4gcHJpb3JpdGlzZSBiZXR3ZWVuIGRpZmZlcmVu
dCBMMkNBUCBjb25uZWN0aW9ucyBvbiB0aGUgc2FtZSBBQ0wgaGFuZGxlLg0KDQo+IEN1cnJlbnRs
eSBJIGZvdW5kIHRoZXJlIGlzIG5vIGZpbHRlciBmb3IgdGhlIHNwZWNpZmljIHBhY2tldHMgZXhj
ZXB0IA0KPiBjaGVja2luZyBwYWNrZXRzIGZyb20gaGNpIHJhdy9tb25pdG9yIHNvY2tldCBvciBo
YWNrIGluIGJ0dXNiLmMgSWYgSSBtaXNzIHNvbWV0aGluZywgcGxlYXNlIGxldCBtZSBrbm93LCB0
aGFua3MuDQoNClRoZXJlIGlzIG5vdGhpbmcgZm9yIGl0IHNpbmNlIGl0IHdhcyBuZXZlciBuZWVk
ZWQuIEFzIEkgc2FpZCwgZG9pbmcgdGhpcyBmb3IgQmx1ZXRvb3RoIHZzIEJsdWV0b290aCBjb25u
ZWN0aW9ucyBpbnNpZGUgdGhlIGZpcm13YXJlLCB0aGF0IGlzIHRvdGFsbHkgcG9pbnRsZXNzLiBK
dXN0IHRydXN0IHRoZSBob3N0IHN0YWNrIHRoYXQgaXQgc2VuZHMgeW91IHRoZSBwYWNrZXRzIGlu
IHRoZSByaWdodCBvcmRlci4gV2hpY2ggaXMgd2hhdCB3ZSBkby4NCg0KRm9yIFdpRmkgc2hhcmlu
ZyB0aGUgc2FtZSBhbnRlbm5hLCBJIGFtIGZpbmUgYWRkaW5nIGV4dHJhIGNhbGxiYWNrcyB0byB0
aGUgZHJpdmVyLiBIb3dldmVyIHRoaXMgaGFzIHRvIGJlIGludGVybmFsIGFuZCBoYW5kZWQgdG8g
dGhlIGRyaXZlciBhcyBleHRyYSBpbmZvcm1hdGlvbi4gQXMgSSBzYWlkIGFib3ZlLCB3ZSBhbHJl
YWR5IGhhdmUgU09fUFJJT1JJVFkgYW5kIHNvIHdlIGtub3cgd2hpY2ggQUNMIGhhbmRsZSBnZXRz
IHByaW9yaXR5IG92ZXIgYW5vdGhlci4gV2UgYWN0dWFsbHkgYWxzbyBrbm93IHRoaXMgZm9yIEwy
Q0FQIGNvbm5lY3Rpb25zIGFuZCBSRkNPTU0gY2hhbm5lbHMgc2luY2UgaXQgd29ya3MgYWxsIHRo
ZSB3YXkgdGhyb3VnaCB0aGUgc3RhY2suDQoNCj4gRm9yIHRoZSB2ZW5kb3IgY29tbWFuZHMgSENJ
X1ZFTkRPUl9TRVRfUEZfUkVQT1JUX0NNRCBhbmQgSENJX1ZFTkRPUl9TRVRfQklUUE9PTF9DTUQs
IHRoZSBmb3JtZXIgaXMgdXNlZCB0byBzZW5kIEJUIHByb2ZpbGVzIGluZm9ybWF0aW9uIHRvIGZp
cm13YXJlLg0KPiBUaGUgb3RoZXIgaXMgdXNlZCB0byBzZW5kIGJpdHBvb2wgdmFsdWUoZnJvbSBh
MmRwKSB0byBmaXJtd2FyZS4NCg0KSSB3YW50IHRoZSBmdWxsIGRlc2NyaXB0aW9uIGhlcmUuIEVz
cGVjaWFsbHkgdGhlIFNFVF9QRl9SRVBPUlRfQ01ELiBXaGF0IGFyZSB0aGUgcGFyYW1ldGVycyBh
bmQgaG93IGlzIGl0IHN1cHBvc2UgdG8gd29yay4gSG93IG9mdGVuIGRvZXMgaXQgbmVlZCB0byBi
ZSB1c2VkLiBIb3cgdG8gY2xlYXIgdGhhdCBpbmZvcm1hdGlvbiwgZG8gdGhleSBzdXJ2aXZlIEhD
SV9SZXNldCBldGMuIFRoZSBiZXN0IHdheSBpcyBpZiB5b3Ugc2VuZCBtZSB0aGUgUmVhbHRlayBI
Q0kgdmVuZG9yIGRvY3VtZW50YXRpb24gKHByaXZhdGVseSBpZiB5b3Ugd2FudCB0bykuDQoNCkFz
IEkgc2FpZCwgSSBzZWUgdGhlIHBvaW50IGZvciBXaUZpIHZzIEJsdWV0b290aCBpZiB5b3Ugc2hh
cmUgdGhlIHNhbWUgYW50ZW5uYS4gQW5kIHdlIGNhbiBjZXJ0YWlubHkgbWFrZSB0aGlzIHdvcmsu
IEkgYW0gaG93ZXZlciBub3QgdHJ5aW5nIHRvIG9wdGltaXNlIEJsdWV0b290aCB2cyBCbHVldG9v
dGggc2luY2UgdGhhdCBhY3R1YWxseSBkb2VzIHdvcmsgY29ycmVjdGx5Lg0KDQpGb3IgdGhlIFNC
QyBiaXRwb29sIHNldHRpbmcgb2YgQTJEUCwgSSBhbSBub3QgZXZlbiBzdXJlIG9uIGhvdyB0aGF0
IGlzIHJlbGV2YW50LCBidXQgSSB3b3VsZCBjbGVhcmx5IGRvIHRoYXQgYXMgYSBzZWNvbmQgcGF0
Y2ggKGlmIGV2ZXIpLg0KDQpSZWdhcmRzDQoNCk1hcmNlbA0KDQo=
Hi Alex,
> First, let me explain the BT profiling.
> BT profiling parses some packets like connection complete event, l2cap conn req/rsp, ... to get profiles information for firmware.
> Firmware uses the information to balance packet transmission priority to get good user experience.
> The typical scenarios:
> 1. a2dp transmission and opp transmission at the same time.
that should work just fine with SO_PRIORITY. There is no need for the firmware to play tricks. Just trust that we prioritise the packets correctly. Which we already do.
> 2. a2dp transmission and WiFi data transmission with one shared antenna at the same time.
> a2dp transmission needs higher priority.
I wonder why not use the SO_PRIORITY setting we have anyway. It is actually more precise than what you have based on ACL handles. Since it will even prioritise between different L2CAP connections on the same ACL handle.
> Currently I found there is no filter for the specific packets except checking packets from hci raw/monitor socket or hack in btusb.c
> If I miss something, please let me know, thanks.
There is nothing for it since it was never needed. As I said, doing this for Bluetooth vs Bluetooth connections inside the firmware, that is totally pointless. Just trust the host stack that it sends you the packets in the right order. Which is what we do.
For WiFi sharing the same antenna, I am fine adding extra callbacks to the driver. However this has to be internal and handed to the driver as extra information. As I said above, we already have SO_PRIORITY and so we know which ACL handle gets priority over another. We actually also know this for L2CAP connections and RFCOMM channels since it works all the way through the stack.
> For the vendor commands HCI_VENDOR_SET_PF_REPORT_CMD and HCI_VENDOR_SET_BITPOOL_CMD, the former is used to send BT profiles information to firmware.
> The other is used to send bitpool value(from a2dp) to firmware.
I want the full description here. Especially the SET_PF_REPORT_CMD. What are the parameters and how is it suppose to work. How often does it need to be used. How to clear that information, do they survive HCI_Reset etc. The best way is if you send me the Realtek HCI vendor documentation (privately if you want to).
As I said, I see the point for WiFi vs Bluetooth if you share the same antenna. And we can certainly make this work. I am however not trying to optimise Bluetooth vs Bluetooth since that actually does work correctly.
For the SBC bitpool setting of A2DP, I am not even sure on how that is relevant, but I would clearly do that as a second patch (if ever).
Regards
Marcel
SGkgTWFyY2VsLApUaGFua3MgZm9yIHlvdXIgcmVzcG9uc2UuCgpGaXJzdCwgbGV0IG1lIGV4cGxh
aW4gdGhlIEJUIHByb2ZpbGluZy4KQlQgcHJvZmlsaW5nIHBhcnNlcyBzb21lIHBhY2tldHMgbGlr
ZSBjb25uZWN0aW9uIGNvbXBsZXRlIGV2ZW50LCBsMmNhcCBjb25uIHJlcS9yc3AsIC4uLiB0byBn
ZXQgcHJvZmlsZXMgaW5mb3JtYXRpb24gZm9yIGZpcm13YXJlLgpGaXJtd2FyZSB1c2VzIHRoZSBp
bmZvcm1hdGlvbiB0byBiYWxhbmNlIHBhY2tldCB0cmFuc21pc3Npb24gcHJpb3JpdHkgdG8gZ2V0
IGdvb2QgdXNlciBleHBlcmllbmNlLgpUaGUgdHlwaWNhbCBzY2VuYXJpb3M6CjEuIGEyZHAgdHJh
bnNtaXNzaW9uIGFuZCBvcHAgdHJhbnNtaXNzaW9uIGF0IHRoZSBzYW1lIHRpbWUuCjIuIGEyZHAg
dHJhbnNtaXNzaW9uIGFuZCBXaUZpIGRhdGEgdHJhbnNtaXNzaW9uIHdpdGggb25lIHNoYXJlZCBh
bnRlbm5hIGF0IHRoZSBzYW1lIHRpbWUuCmEyZHAgdHJhbnNtaXNzaW9uIG5lZWRzIGhpZ2hlciBw
cmlvcml0eS4KCkN1cnJlbnRseSBJIGZvdW5kIHRoZXJlIGlzIG5vIGZpbHRlciBmb3IgdGhlIHNw
ZWNpZmljIHBhY2tldHMgZXhjZXB0IGNoZWNraW5nIHBhY2tldHMgZnJvbSBoY2kgcmF3L21vbml0
b3Igc29ja2V0IG9yIGhhY2sgaW4gYnR1c2IuYwpJZiBJIG1pc3Mgc29tZXRoaW5nLCBwbGVhc2Ug
bGV0IG1lIGtub3csIHRoYW5rcy4KCkZvciB0aGUgdmVuZG9yIGNvbW1hbmRzIEhDSV9WRU5ET1Jf
U0VUX1BGX1JFUE9SVF9DTUQgYW5kIEhDSV9WRU5ET1JfU0VUX0JJVFBPT0xfQ01ELCB0aGUgZm9y
bWVyIGlzIHVzZWQgdG8gc2VuZCBCVCBwcm9maWxlcyBpbmZvcm1hdGlvbiB0byBmaXJtd2FyZS4K
VGhlIG90aGVyIGlzIHVzZWQgdG8gc2VuZCBiaXRwb29sIHZhbHVlKGZyb20gYTJkcCkgdG8gZmly
bXdhcmUuCgpUaGFua3MsCkJScywKQWxleCBMdS4KX19fX19fX19fX19fX19fX19fX19fX19fX19f
X19fX19fX19fX19fXwq3orz+yMs6IE1hcmNlbCBIb2x0bWFubiBbbWFyY2VsQGhvbHRtYW5uLm9y
Z10Kt6LLzcqxvOQ6IDIwMTfE6jLUwjEwyNUgMTk6MDkKytW8/sjLOiBMYXJyeSBGaW5nZXIKs63L
zTogR3VzdGF2byBGLiBQYWRvdmFuOyBKb2hhbiBIZWRiZXJnOyBsaW51eC1ibHVldG9vdGg7IMK9
1uzOsDsgbGludXgta2VybmVsQHZnZXIua2VybmVsLm9yZwrW98ziOiBSZTogW1BBVENIXSBydGxi
dDogQWRkIFJlYWx0ZWsgQmx1ZXRvb3RoIHByb2ZpbGluZyBzdXBwb3J0CgpIaSBMYXJyeSwKCj4g
QWRkIHRoZSBSZWFsdGVrIEJsdWV0b290aCBwcm9maWxlIHByb2ZpbGluZyBzdXBwb3J0IHRvIGNy
ZWF0ZQo+IHByb2ZpbGUgaW5mb3JtYXRpb24sIHdoaWNoIGhlbHBzIHRoZSBmaXJtd2FyZSBvcHRp
bWl6ZSB0cmFuc2Zlcgo+IHByaW9yaXR5IGFuZCBiYWxhbmNlIHRoZSB0cmFuc21pc3Npb25zIGZv
ciBtdWx0aXBsZSBwcm9maWxlcy4KPgo+IFNpZ25lZC1vZmYtYnk6IEFsZXggTHUgPGFsZXhfbHVA
cmVhbHNpbC5jb20uY24+Cj4gU2lnbmVkLW9mZi1ieTogTGFycnkgRmluZ2VyIDxMYXJyeS5GaW5n
ZXJAbHdmaW5nZXIubmV0Pgo+IC0tLQo+IGRyaXZlcnMvYmx1ZXRvb3RoL0tjb25maWcgICAgfCAg
IDE2ICsKPiBkcml2ZXJzL2JsdWV0b290aC9NYWtlZmlsZSAgIHwgICAgMSArCj4gZHJpdmVycy9i
bHVldG9vdGgvYnR1c2IuYyAgICB8ICAgMTIgKwo+IGRyaXZlcnMvYmx1ZXRvb3RoL3J0bF9idHBm
LmMgfCAxMjQ5ICsrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKwo+IGRy
aXZlcnMvYmx1ZXRvb3RoL3J0bF9idHBmLmggfCAgMTg0ICsrKysrKysKPiA1IGZpbGVzIGNoYW5n
ZWQsIDE0NjIgaW5zZXJ0aW9ucygrKQo+IGNyZWF0ZSBtb2RlIDEwMDY0NCBkcml2ZXJzL2JsdWV0
b290aC9ydGxfYnRwZi5jCj4gY3JlYXRlIG1vZGUgMTAwNjQ0IGRyaXZlcnMvYmx1ZXRvb3RoL3J0
bF9idHBmLmgKPgoKPHNuaXA+Cgo+ICtzdGF0aWMgaW50IGJ0cGZfb3Blbl9zb2NrZXQoc3RydWN0
IHJ0bF9idHBmICpidHBmKQo+ICt7Cj4gKyAgICAgaW50IHJldDsKPiArICAgICBzdHJ1Y3Qgc29j
a2FkZHJfaGNpIGFkZHI7Cj4gKyAgICAgc3RydWN0IHNvY2sgKnNrOwo+ICsgICAgIHN0cnVjdCBo
Y2lfZmlsdGVyIGZsdDsKPiArCj4gKyAgICAgcmV0ID0gc29ja19jcmVhdGVfa2VybigmaW5pdF9u
ZXQsIFBGX0JMVUVUT09USCwgU09DS19SQVcsIEJUUFJPVE9fSENJLAo+ICsgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgJmJ0cGYtPmhjaV9zb2NrKTsKPiArICAgICBpZiAocmV0IDwgMCkgewo+
ICsgICAgICAgICAgICAgcnRsYnRfZXJyKCJDcmVhdGUgaGNpIHNvY2sgZXJyb3IgJWQiLCByZXQp
Owo+ICsgICAgICAgICAgICAgZ290byBlcnJfMTsKPiArICAgICB9Cj4gKwo+ICsgICAgIG1lbXNl
dCgmYWRkciwgMCwgc2l6ZW9mKGFkZHIpKTsKPiArICAgICBhZGRyLmhjaV9mYW1pbHkgPSBBRl9C
TFVFVE9PVEg7Cj4gKyAgICAgLyogQXNzdW1lIFJlYWx0ZWsgQlQgY29udHJvbGxlciBpbmRleCBp
cyAwLiAqLwo+ICsgICAgIGFkZHIuaGNpX2RldiA9IDA7Cj4gKyAgICAgYWRkci5oY2lfY2hhbm5l
bCA9IEhDSV9DSEFOTkVMX1JBVzsKPiArICAgICByZXQgPSBrZXJuZWxfYmluZChidHBmLT5oY2lf
c29jaywgKHN0cnVjdCBzb2NrYWRkciAqKSZhZGRyLAo+ICsgICAgICAgICAgICAgICAgICAgICAg
IHNpemVvZihhZGRyKSk7Cj4gKyAgICAgaWYgKHJldCA8IDApIHsKPiArICAgICAgICAgICAgIHJ0
bGJ0X2VycigiQmluZCBoY2kgc29jayBlcnJvciIpOwo+ICsgICAgICAgICAgICAgZ290byBlcnJf
MjsKPiArICAgICB9CgpsZXQgbWUgc3RhcnQgd2l0aCBhIGNsZWFyIGFuc3dlciBoZXJlLiBUaGlz
IGlzIGluc2FuZSBhbmQgd2lsbCBuZXZlciBiZSBtZXJnZWQgdXBzdHJlYW0uCgpJIGhhdmUgbm8g
aWRlYSBvbiBob3cgZGVzY3JpYmUgdGhpcyBhbnkgb3RoZXIgd2F5LCBidXQgdGhpcyBpcyByZWFs
bHkgYmFkIGlkZWEsIHRvdGFsbHkgYmxvYXRlZCBhbmQgY2F1c2luZyBzaWRlIGFmZmVjdHMgbGVm
dCBhbmQgcmlnaHQuIFRoZSBIQ0kgcmF3IHNvY2tldCBpcyBub3QgZm9yIGRvaW5nIHByb2ZpbGlu
Zy4gRXNwZWNpYWxseSBub3QgaW5zaWRlIHRoZSBrZXJuZWwuIEFuZCBqdXN0IHRoaW5raW5nIHRo
YXQgc29tZW9uZSB0b29rIHRoZSBvbGQgaGNpZHVtcCBjb2RlIGFuZCBoYWNrZWQgaXQgdG8gcnVu
IGFzIGEga2VybmVsIG1vZHVsZSBpcyBzb21ldGhpbmcgSSBuZXZlciBleHBlY3RlZCB0byBoYXBw
ZW4uCgo8c25pcD4KCj4gKwo+ICsjZGVmaW5lIEhDSV9WRU5ET1JfU0VUX1BGX1JFUE9SVF9DTUQg
MHhmYzE5Cj4gKyNkZWZpbmUgSENJX1ZFTkRPUl9TRVRfQklUUE9PTF9DTUQgICAweGZjNTEKCkkg
dGhpbmsgdGhhdCBldmVyeXRoaW5nIHNob3VsZCBzdGFydCB3aXRoIHdoYXQgdGhlc2UgdHdvIHZl
bmRvciBjb21tYW5kcyBhcmUgYWN0dWFsbHkgZG9pbmcuIFNvIGEgc2ltcGxlIGRlc2NyaXB0aW9u
cyBvZiB0aGVzZSB0d28gdmVuZG9yIGNvbW1hbmRzIGlzIHdoYXQgaXMgbmVlZGVkIGZpcnN0LgoK
QW5kIHRoZW4gc29tZW9uZSBuZWVkcyB0byB0ZWxsIG1lIHdoYXQgYWxsIHRoaXMgY29kZSBkb2Vz
IHRoYXQgU09fUFJJT1JJVFkgaXMgbm90IGFscmVhZHkgZG9pbmcgYW5kIGhhcyBiZWVuIGRvaW5n
IGZvciBhIGxvbmcgdGltZS4KClJlZ2FyZHMKCk1hcmNlbAoK
Hi Larry,
> Add the Realtek Bluetooth profile profiling support to create
> profile information, which helps the firmware optimize transfer
> priority and balance the transmissions for multiple profiles.
>
> Signed-off-by: Alex Lu <[email protected]>
> Signed-off-by: Larry Finger <[email protected]>
> ---
> drivers/bluetooth/Kconfig | 16 +
> drivers/bluetooth/Makefile | 1 +
> drivers/bluetooth/btusb.c | 12 +
> drivers/bluetooth/rtl_btpf.c | 1249 ++++++++++++++++++++++++++++++++++++++++++
> drivers/bluetooth/rtl_btpf.h | 184 +++++++
> 5 files changed, 1462 insertions(+)
> create mode 100644 drivers/bluetooth/rtl_btpf.c
> create mode 100644 drivers/bluetooth/rtl_btpf.h
>
<snip>
> +static int btpf_open_socket(struct rtl_btpf *btpf)
> +{
> + int ret;
> + struct sockaddr_hci addr;
> + struct sock *sk;
> + struct hci_filter flt;
> +
> + ret = sock_create_kern(&init_net, PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI,
> + &btpf->hci_sock);
> + if (ret < 0) {
> + rtlbt_err("Create hci sock error %d", ret);
> + goto err_1;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.hci_family = AF_BLUETOOTH;
> + /* Assume Realtek BT controller index is 0. */
> + addr.hci_dev = 0;
> + addr.hci_channel = HCI_CHANNEL_RAW;
> + ret = kernel_bind(btpf->hci_sock, (struct sockaddr *)&addr,
> + sizeof(addr));
> + if (ret < 0) {
> + rtlbt_err("Bind hci sock error");
> + goto err_2;
> + }
let me start with a clear answer here. This is insane and will never be merged upstream.
I have no idea on how describe this any other way, but this is really bad idea, totally bloated and causing side affects left and right. The HCI raw socket is not for doing profiling. Especially not inside the kernel. And just thinking that someone took the old hcidump code and hacked it to run as a kernel module is something I never expected to happen.
<snip>
> +
> +#define HCI_VENDOR_SET_PF_REPORT_CMD 0xfc19
> +#define HCI_VENDOR_SET_BITPOOL_CMD 0xfc51
I think that everything should start with what these two vendor commands are actually doing. So a simple descriptions of these two vendor commands is what is needed first.
And then someone needs to tell me what all this code does that SO_PRIORITY is not already doing and has been doing for a long time.
Regards
Marcel
Hi Larry,
On Thu, Feb 9, 2017 at 8:23 PM, Larry Finger <[email protected]> wrote:
> From: Alex Lu <[email protected]>
>
> Add the Realtek Bluetooth profile profiling support to create
> profile information, which helps the firmware optimize transfer
> priority and balance the transmissions for multiple profiles.
This sounds overkill, we do have plans to have some sort of tag to
indicate what each socket is transporting but then going as far as
doing profile parsing, I don't think this belongs to the kernel.
> Signed-off-by: Alex Lu <[email protected]>
> Signed-off-by: Larry Finger <[email protected]>
> ---
> drivers/bluetooth/Kconfig | 16 +
> drivers/bluetooth/Makefile | 1 +
> drivers/bluetooth/btusb.c | 12 +
> drivers/bluetooth/rtl_btpf.c | 1249 ++++++++++++++++++++++++++++++++++++++++++
> drivers/bluetooth/rtl_btpf.h | 184 +++++++
> 5 files changed, 1462 insertions(+)
> create mode 100644 drivers/bluetooth/rtl_btpf.c
> create mode 100644 drivers/bluetooth/rtl_btpf.h
>
> diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
> index 3cc9bff..354f852 100644
> --- a/drivers/bluetooth/Kconfig
> +++ b/drivers/bluetooth/Kconfig
> @@ -14,6 +14,9 @@ config BT_RTL
> tristate
> select FW_LOADER
>
> +config BT_RTL_BTPF
> + tristate
> +
> config BT_QCA
> tristate
> select FW_LOADER
> @@ -52,6 +55,19 @@ config BT_HCIBTUSB_RTL
>
> Say Y here to compile support for Realtek protocol.
>
> +config BT_HCIBTUSB_RTL_BTPF
> + bool "Realtek profiling support"
> + depends on BT_HCIBTUSB && BT_RTL
> + select BT_RTL_BTPF
> + default y
> + help
> + This parameter adds Realtek Bluetooth profile profiling support
> + that enables the gathering of profile information, which helps
> + the firmware optimize transfer priority and balance the transmissions
> + for multiple profiles.
> +
> + Say Y here to compile support for Realtek profiling.
> +
> config BT_HCIBTSDIO
> tristate "HCI SDIO driver"
> depends on MMC
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index 8062718..a3dd8a4 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
> obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
> obj-$(CONFIG_BT_BCM) += btbcm.o
> obj-$(CONFIG_BT_RTL) += btrtl.o
> +obj-$(CONFIG_BT_RTL_BTPF) += rtl_btpf.o
> obj-$(CONFIG_BT_QCA) += btqca.o
>
> btmrvl-y := btmrvl_main.o
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index 2f633df..bc1c923 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -33,6 +33,10 @@
> #include "btbcm.h"
> #include "btrtl.h"
>
> +#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
> +#include "rtl_btpf.h"
> +#endif
> +
> #define VERSION "0.8"
>
> static bool disable_scofix;
> @@ -3023,6 +3027,10 @@ static int btusb_probe(struct usb_interface *intf,
>
> usb_set_intfdata(intf, data);
>
> +#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
> + rtl_btpf_init();
> +#endif
> +
> return 0;
> }
>
> @@ -3045,6 +3053,10 @@ static void btusb_disconnect(struct usb_interface *intf)
> if (data->diag)
> usb_set_intfdata(data->diag, NULL);
>
> +#ifdef CONFIG_BT_HCIBTUSB_RTL_BTPF
> + rtl_btpf_deinit();
> +#endif
> +
> hci_unregister_dev(hdev);
>
> if (intf == data->intf) {
> diff --git a/drivers/bluetooth/rtl_btpf.c b/drivers/bluetooth/rtl_btpf.c
> new file mode 100644
> index 0000000..a2d19b6
> --- /dev/null
> +++ b/drivers/bluetooth/rtl_btpf.c
> @@ -0,0 +1,1249 @@
> +/*
> + *
> + * Realtek Bluetooth Profile profiling driver
> + *
> + * Copyright (C) 2015 Realtek Semiconductor Corporation
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/sched.h>
> +#include <linux/errno.h>
> +#include <linux/skbuff.h>
> +#include <linux/usb.h>
> +#include <linux/dcache.h>
> +#include <linux/version.h>
> +#include <linux/skbuff.h>
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/l2cap.h>
> +#include <net/bluetooth/hci_mon.h>
> +#include "rtl_btpf.h"
> +
> +#define VERSION "0.1"
> +
> +#define BTPF_CMD_MAXLEN 64
> +
> +
> +static struct rtl_btpf *rtl_btpf;
> +
> +static int psm_to_profile(u16 psm)
> +{
> + switch (psm) {
> + case PSM_AVCTP:
> + case PSM_SDP:
> + return -1; /* ignore */
> +
> + case PSM_HID:
> + case PSM_HID_INT:
> + return PROFILE_HID;
> +
> + case PSM_AVDTP:
> + return PROFILE_A2DP;
> +
> + case PSM_PAN:
> + case PSM_OPP:
> + case PSM_FTP:
> + case PSM_BIP:
> + case PSM_RFCOMM:
> + return PROFILE_PAN;
> +
> + default:
> + return PROFILE_PAN;
> + }
> +}
> +
> +static struct rtl_hci_conn *rtl_hci_conn_lookup(struct rtl_btpf *btpf,
> + u16 handle)
> +{
> + struct list_head *head = &btpf->conn_list;
> + struct list_head *p, *n;
> + struct rtl_hci_conn *conn;
> +
> + list_for_each_safe(p, n, head) {
> + conn = list_entry(p, struct rtl_hci_conn, list);
> + if ((handle & 0xfff) == conn->handle)
> + return conn;
> + }
> +
> + return NULL;
> +}
> +
> +static void rtl_hci_conn_list_purge(struct rtl_btpf *btpf)
> +{
> + struct list_head *head = &btpf->conn_list;
> + struct list_head *p, *n;
> + struct rtl_hci_conn *conn;
> +
> + list_for_each_safe(p, n, head) {
> + conn = list_entry(p, struct rtl_hci_conn, list);
> + if (conn) {
> + list_del(&conn->list);
> + kfree(conn);
> + }
> + }
> +}
> +
> +static struct rtl_profile *profile_alloc(u16 handle, u16 psm, u8 idx,
> + u16 dcid, u16 scid)
> +{
> + struct rtl_profile *pf;
> +
> + pf = kzalloc(sizeof(struct rtl_profile), GFP_KERNEL);
> + if (!pf)
> + return NULL;
> +
> + pf->handle = handle;
> + pf->psm = psm;
> + pf->scid = scid;
> + pf->dcid = dcid;
> + pf->idx = idx;
> + INIT_LIST_HEAD(&pf->list);
> +
> + return pf;
> +}
> +
> +static void rtl_profile_list_purge(struct rtl_btpf *btpf)
> +{
> + struct list_head *head = &btpf->pf_list;
> + struct list_head *p, *n;
> + struct rtl_profile *pf;
> +
> + list_for_each_safe(p, n, head) {
> + pf = list_entry(p, struct rtl_profile, list);
> + list_del(&pf->list);
> + kfree(pf);
> + }
> +}
> +
> +static struct rtl_profile *rtl_profile_lookup(struct rtl_btpf *btpf,
> + struct rtl_profile_id *id)
> +{
> + struct list_head *head = &btpf->pf_list;
> + struct list_head *p, *n;
> + struct rtl_profile *tmp;
> + u16 handle = id->handle;
> +
> + if (!id->match_flags) {
> + rtlbt_warn("%s: no match flags", __func__);
> + return NULL;
> + }
> +
> + list_for_each_safe(p, n, head) {
> + tmp = list_entry(p, struct rtl_profile, list);
> +
> + if ((id->match_flags & RTL_PROFILE_MATCH_HANDLE) &&
> + (handle & 0xfff) != tmp->handle)
> + continue;
> +
> + if ((id->match_flags & RTL_PROFILE_MATCH_SCID) &&
> + id->scid != tmp->scid)
> + continue;
> +
> + if ((id->match_flags & RTL_PROFILE_MATCH_DCID) &&
> + id->dcid != tmp->dcid)
> + continue;
> +
> + return tmp;
> + }
> +
> + return NULL;
> +}
> +
> +static int hci_cmd_send_to_fw(struct rtl_btpf *btpf, u16 opcode, u8 dlen,
> + u8 *data)
> +{
> + int n = 1 + 3 + dlen;
> + u8 buff[BTPF_CMD_MAXLEN];
> + struct kvec iv = { buff, n };
> + struct msghdr msg;
> + int ret;
> +
> + if (!test_bit(BTPF_HCI_SOCK, &btpf->flags) ||
> + !test_bit(BTPF_CID_RTL, &btpf->flags))
> + return -1;
> +
> + rtlbt_info("%s: opcode 0x%04x", __func__, opcode);
> + if (n > BTPF_CMD_MAXLEN) {
> + rtlbt_err("vendor cmd too large");
> + return -1;
> + }
> +
> + buff[0] = HCI_COMMAND_PKT;
> + buff[1] = opcode & 0xff;
> + buff[2] = (opcode >> 8) & 0xff;
> + buff[3] = dlen;
> + memcpy(buff + 4, data, dlen);
> +
> + memset(&msg, 0, sizeof(msg));
> +
> + ret = kernel_sendmsg(btpf->hci_sock, &msg, &iv, 1, n);
> + if (ret < 0) {
> + rtlbt_err("sendmsg failed: %d", ret);
> + return -EAGAIN;
> + }
> +
> + return 0;
> +}
> +
> +static void btpf_update_to_controller(struct rtl_btpf *btpf)
> +{
> + struct list_head *head, *pos, *q;
> + struct rtl_hci_conn *conn;
> + u8 handle_num;
> + u32 buff_sz;
> + u8 *buff;
> + u8 *p;
> +
> + if (!test_bit(BTPF_CID_RTL, &btpf->flags))
> + return;
> +
> + head = &btpf->conn_list;
> + handle_num = 0;
> + list_for_each_safe(pos, q, head) {
> + conn = list_entry(pos, struct rtl_hci_conn, list);
> + if (conn && conn->pf_bits)
> + handle_num++;
> + }
> +
> + buff_sz = 1 + handle_num * 3 + 1;
> +
> + rtlbt_info("%s: buff_sz %u, handle_num %u", __func__, buff_sz,
> + handle_num);
> +
> + buff = kzalloc(buff_sz, GFP_ATOMIC);
> + if (!buff)
> + return;
> +
> + p = buff;
> + *p++ = handle_num;
> + head = &btpf->conn_list;
> + list_for_each(pos, head) {
> + conn = list_entry(pos, struct rtl_hci_conn, list);
> + if (conn && conn->pf_bits) {
> + put_unaligned_le16(conn->handle, p);
> + p += 2;
> + rtlbt_info("%s: handle 0x%04x, pf_bits 0x%02x",
> + __func__, conn->handle, conn->pf_bits);
> + *p++ = conn->pf_bits;
> + handle_num--;
> + }
> + if (!handle_num)
> + break;
> + }
> + *p++ = btpf->pf_state;
> +
> + rtlbt_info("%s: pf_state 0x%02x", __func__, btpf->pf_state);
> +
> + hci_cmd_send_to_fw(btpf, HCI_VENDOR_SET_PF_REPORT_CMD, buff_sz, buff);
> +
> + kfree(buff);
> +}
> +
> +static void update_profile_state(struct rtl_btpf *btpf, u8 idx, u8 busy)
> +{
> + u8 update = 0;
> +
> + if (!(btpf->pf_bits & BIT(idx))) {
> + rtlbt_err("%s: profile(%x) not exist", __func__, idx);
> + return;
> + }
> +
> + if (busy) {
> + if (!(btpf->pf_state & BIT(idx))) {
> + update = 1;
> + btpf->pf_state |= BIT(idx);
> + }
> + } else {
> + if (btpf->pf_state & BIT(idx)) {
> + update = 1;
> + btpf->pf_state &= ~BIT(idx);
> + }
> + }
> +
> + if (update) {
> + rtlbt_info("%s: pf_bits 0x%02x", __func__, btpf->pf_bits);
> + rtlbt_info("%s: pf_state 0x%02x", __func__, btpf->pf_state);
> + btpf_update_to_controller(btpf);
> + }
> +}
> +
> +static void a2dp_do_poll(unsigned long data)
> +{
> + struct rtl_btpf *btpf = (struct rtl_btpf *)data;
> +
> + rtlbt_dbg("%s: icount.a2dp %d", __func__, btpf->icount.a2dp);
> +
> + if (!btpf->icount.a2dp) {
> + if (btpf->pf_state & BIT(PROFILE_A2DP)) {
> + rtlbt_info("%s: a2dp state, busy to idle", __func__);
> + update_profile_state(btpf, PROFILE_A2DP, 0);
> + }
> + }
> +
> + btpf->icount.a2dp = 0;
> + mod_timer(&btpf->a2dp_timer, jiffies + msecs_to_jiffies(1000));
> +}
> +
> +static void pan_do_poll(unsigned long data)
> +{
> + struct rtl_btpf *btpf = (struct rtl_btpf *)data;
> +
> + rtlbt_dbg("%s: icount.pan %d", __func__, btpf->icount.pan);
> +
> + if (btpf->icount.pan < PAN_PACKET_COUNT) {
> + if (btpf->pf_state & BIT(PROFILE_PAN)) {
> + rtlbt_info("%s: pan state, busy to idle", __func__);
> + update_profile_state(btpf, PROFILE_PAN, 0);
> + }
> + } else {
> + if (!(btpf->pf_state & BIT(PROFILE_PAN))) {
> + rtlbt_info("%s: pan state, idle to busy", __func__);
> + update_profile_state(btpf, PROFILE_PAN, 1);
> + }
> + }
> +
> + btpf->icount.pan = 0;
> + mod_timer(&btpf->pan_timer, jiffies + msecs_to_jiffies(1000));
> +}
> +
> +static void setup_monitor_timer(struct rtl_btpf *btpf, u8 idx)
> +{
> + switch (idx) {
> + case PROFILE_A2DP:
> + btpf->icount.a2dp = 0;
> + setup_timer(&btpf->a2dp_timer, a2dp_do_poll,
> + (unsigned long)btpf);
> + btpf->a2dp_timer.expires = jiffies + msecs_to_jiffies(1000);
> + add_timer(&btpf->a2dp_timer);
> + break;
> + case PROFILE_PAN:
> + btpf->icount.pan = 0;
> + setup_timer(&btpf->pan_timer, pan_do_poll, (unsigned long)btpf);
> + btpf->pan_timer.expires = jiffies + msecs_to_jiffies(1000);
> + add_timer(&(btpf->pan_timer));
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static void del_monitor_timer(struct rtl_btpf *btpf, u8 idx)
> +{
> + switch (idx) {
> + case PROFILE_A2DP:
> + btpf->icount.a2dp = 0;
> + del_timer_sync(&btpf->a2dp_timer);
> + break;
> + case PROFILE_PAN:
> + btpf->icount.pan = 0;
> + del_timer_sync(&btpf->pan_timer);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static int profile_conn_get(struct rtl_btpf *btpf, struct rtl_hci_conn *conn,
> + u8 idx)
> +{
> + int update = 0;
> + u8 i;
> +
> + rtlbt_dbg("%s: idx %u", __func__, idx);
> +
> + if (!conn || idx >= PROFILE_MAX)
> + return -EINVAL;
> +
> + if (!btpf->pf_refs[idx]) {
> + update = 1;
> + btpf->pf_bits |= BIT(idx);
> +
> + /* SCO is always busy */
> + if (idx == PROFILE_SCO)
> + btpf->pf_state |= BIT(idx);
> +
> + setup_monitor_timer(btpf, idx);
> + }
> + btpf->pf_refs[idx]++;
> +
> + if (!conn->pf_refs[idx]) {
> + update = 1;
> + conn->pf_bits |= BIT(idx);
> + }
> + conn->pf_refs[idx]++;
> +
> + rtlbt_info("%s: btpf->pf_bits 0x%02x", __func__, btpf->pf_bits);
> + for (i = 0; i < MAX_PROFILE_NUM; i++)
> + rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, i,
> + btpf->pf_refs[i]);
> +
> + if (update)
> + btpf_update_to_controller(btpf);
> +
> + return 0;
> +}
> +
> +static int profile_conn_put(struct rtl_btpf *btpf, struct rtl_hci_conn *conn,
> + u8 idx)
> +{
> + int need_update = 0;
> + u8 i;
> +
> + rtlbt_dbg("%s: idx %u", __func__, idx);
> +
> + if (!conn || idx >= PROFILE_MAX)
> + return -EINVAL;
> +
> + btpf->pf_refs[idx]--;
> + if (!btpf->pf_refs[idx]) {
> + need_update = 1;
> + btpf->pf_bits &= ~BIT(idx);
> + btpf->pf_state &= ~BIT(idx);
> + del_monitor_timer(btpf, idx);
> + }
> +
> + conn->pf_refs[idx]--;
> + if (!conn->pf_refs[idx]) {
> + need_update = 1;
> + conn->pf_bits &= ~BIT(idx);
> +
> + /* Clear hid interval if needed */
> + if (idx == PROFILE_HID &&
> + (conn->pf_bits & BIT(PROFILE_HID2))) {
> + conn->pf_bits &= ~BIT(PROFILE_HID2);
> + btpf->pf_refs[PROFILE_HID2]--;
> + }
> + }
> +
> + rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, idx,
> + btpf->pf_refs[idx]);
> + rtlbt_info("%s: pf_bits 0x%02x", __func__, btpf->pf_bits);
> + for (i = 0; i < MAX_PROFILE_NUM; i++)
> + rtlbt_info("%s: btpf->pf_refs[%u] %d", __func__, i,
> + btpf->pf_refs[i]);
> +
> + if (need_update)
> + btpf_update_to_controller(btpf);
> +
> + return 0;
> +}
> +
> +static void hid_state_update(struct rtl_btpf *btpf, u16 handle,
> + u16 interval)
> +{
> + u8 update = 0;
> + struct rtl_hci_conn *conn;
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn)
> + return;
> +
> + rtlbt_info("%s: handle 0x%04x, interval 0x%x", __func__, handle,
> + interval);
> + if (!(conn->pf_bits & BIT(PROFILE_HID))) {
> + rtlbt_dbg("hid not connected in the handle");
> + return;
> + }
> +
> + if (interval < 60) {
> + if (!(conn->pf_bits & BIT(PROFILE_HID2))) {
> + update = 1;
> + conn->pf_bits |= BIT(PROFILE_HID2);
> +
> + btpf->pf_refs[PROFILE_HID2]++;
> + if (btpf->pf_refs[PROFILE_HID2] == 1)
> + btpf->pf_state |= BIT(PROFILE_HID);
> + }
> + } else {
> + if (conn->pf_bits & BIT(PROFILE_HID2)) {
> + update = 1;
> + conn->pf_bits &= ~BIT(PROFILE_HID2);
> +
> + btpf->pf_refs[PROFILE_HID2]--;
> + if (!btpf->pf_refs[PROFILE_HID2])
> + btpf->pf_state &= ~BIT(PROFILE_HID);
> + }
> + }
> +
> + if (update)
> + btpf_update_to_controller(btpf);
> +}
> +
> +static int handle_l2cap_conn_req(struct rtl_btpf *btpf, u16 handle, u16 psm,
> + u16 cid, u8 dir)
> +{
> + struct rtl_profile *pf;
> + int idx = psm_to_profile(psm);
> + struct rtl_profile_id id;
> +
> + if (idx < 0) {
> + rtlbt_info("no need to parse psm %04x", psm);
> + return 0;
> + }
> +
> + memset(&id, 0, sizeof(id));
> + id.match_flags = RTL_PROFILE_MATCH_HANDLE;
> + id.handle = handle;
> +
> + if (dir == RTL_TO_REMOTE) {
> + id.match_flags |= RTL_PROFILE_MATCH_SCID;
> + id.scid = cid;
> + } else {
> + id.match_flags |= RTL_PROFILE_MATCH_DCID;
> + id.dcid = cid;
> + }
> +
> + pf = rtl_profile_lookup(btpf, &id);
> +
> + if (pf) {
> + rtlbt_warn("%s: profile already exists", __func__);
> + return -1;
> + }
> +
> + if (dir == RTL_TO_REMOTE)
> + pf = profile_alloc(handle, psm, (u8)idx, 0, cid);
> + else
> + pf = profile_alloc(handle, psm, (u8)idx, cid, 0);
> +
> + if (!pf) {
> + rtlbt_err("%s: allocate profile failed", __func__);
> + return -1;
> + }
> +
> + list_add_tail(&pf->list, &btpf->pf_list);
> +
> + return 0;
> +}
> +
> +/* dcid is the cid on the device sending this resp packet.
> + * scid is the cid on the device receiving the resp packet.
> + */
> +static u8 handle_l2cap_conn_rsp(struct rtl_btpf *btpf,
> + u16 handle, u16 dcid,
> + u16 scid, u8 dir, u8 result)
> +{
> + struct rtl_profile *pf;
> + struct rtl_hci_conn *conn;
> + struct rtl_profile_id id = {
> + .match_flags = RTL_PROFILE_MATCH_HANDLE,
> + .handle = handle,
> + };
> +
> + if (dir == RTL_FROM_REMOTE) {
> + id.match_flags |= RTL_PROFILE_MATCH_SCID;
> + id.scid = scid;
> + pf = rtl_profile_lookup(btpf, &id);
> + } else {
> + id.match_flags |= RTL_PROFILE_MATCH_DCID;
> + id.dcid = scid;
> + pf = rtl_profile_lookup(btpf, &id);
> + }
> +
> + if (!pf) {
> + rtlbt_err("%s: profile not found", __func__);
> + return -1;
> + }
> +
> + if (!result) {
> + rtlbt_info("l2cap connection success");
> + if (dir == RTL_FROM_REMOTE)
> + pf->dcid = dcid;
> + else
> + pf->scid = dcid;
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (conn)
> + profile_conn_get(btpf, conn, pf->idx);
> + }
> +
> + return 0;
> +}
> +
> +static int handle_l2cap_disconn_req(struct rtl_btpf *btpf,
> + u16 handle, u16 dcid,
> + u16 scid, u8 dir)
> +{
> + struct rtl_profile *pf;
> + struct rtl_hci_conn *conn;
> + int err = 0;
> + struct rtl_profile_id id = {
> + .match_flags = RTL_PROFILE_MATCH_HANDLE |
> + RTL_PROFILE_MATCH_SCID |
> + RTL_PROFILE_MATCH_DCID,
> + .handle = handle,
> + .scid = scid,
> + .dcid = dcid,
> + };
> +
> + if (dir == RTL_FROM_REMOTE) {
> + id.scid = dcid;
> + id.dcid = scid;
> + pf = rtl_profile_lookup(btpf, &id);
> + } else {
> + pf = rtl_profile_lookup(btpf, &id);
> + }
> +
> + if (!pf) {
> + rtlbt_err("%s: no profile", __func__);
> + err = -1;
> + goto done;
> + }
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn) {
> + rtlbt_err("%s: no connection", __func__);
> + err = -1;
> + goto done;
> + }
> +
> + profile_conn_put(btpf, conn, pf->idx);
> + list_del(&pf->list);
> + kfree(pf);
> +
> +done:
> + rtlbt_info("%s: handle %04x, dcid %04x, scid %04x, dir %x",
> + __func__, handle, dcid, scid, dir);
> +
> + return 0;
> +}
> +
> +static const char sample_freqs[4][8] = {
> + "16", "32", "44.1", "48"
> +};
> +
> +static const u8 sbc_blocks[4] = { 4, 8, 12, 16 };
> +
> +static const char chan_modes[4][16] = {
> + "MONO", "DUAL_CHANNEL", "STEREO", "JOINT_STEREO"
> +};
> +
> +static const char alloc_methods[2][12] = {
> + "LOUDNESS", "SNR"
> +};
> +
> +static const u8 subbands[2] = { 4, 8 };
> +
> +static void pr_sbc_hdr(struct sbc_frame_hdr *hdr)
> +{
> + rtlbt_info("syncword: %02x", hdr->syncword);
> + rtlbt_info("freq %skHz", sample_freqs[hdr->sampling_frequency]);
> + rtlbt_info("blocks %u", sbc_blocks[hdr->blocks]);
> + rtlbt_info("channel mode %s", chan_modes[hdr->channel_mode]);
> + rtlbt_info("allocation method %s",
> + alloc_methods[hdr->allocation_method]);
> + rtlbt_info("subbands %u", subbands[hdr->subbands]);
> +}
> +
> +static void packet_increment(struct rtl_btpf *btpf, u16 handle,
> + u16 ch_id, u16 length, u8 *payload, u8 dir)
> +{
> + struct rtl_profile *pf;
> + struct rtl_hci_conn *conn;
> + struct rtl_profile_id id;
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn)
> + goto done;
> +
> + if (conn->type != ACL_CONN)
> + return;
> +
> + memset(&id, 0, sizeof(id));
> + id.match_flags = RTL_PROFILE_MATCH_HANDLE;
> + id.handle = handle;
> + if (dir == RTL_FROM_REMOTE) {
> + id.match_flags |= RTL_PROFILE_MATCH_SCID;
> + id.scid = ch_id;
> + } else {
> + id.match_flags |= RTL_PROFILE_MATCH_DCID;
> + id.dcid = ch_id;
> + }
> + pf = rtl_profile_lookup(btpf, &id);
> + if (!pf)
> + goto done;
> +
> + if (pf->idx == PROFILE_A2DP && length > 100) {
> + /* avdtp media data */
> + if (!(btpf->pf_state & BIT(PROFILE_A2DP))) {
> + struct sbc_frame_hdr *sbc_hdr;
> + struct rtp_header *rtp_hdr;
> + u8 bitpool;
> +
> + update_profile_state(btpf, PROFILE_A2DP, 1);
> + rtp_hdr = (struct rtp_header *)payload;
> +
> + rtlbt_info("rtp: v %u, cc %u, pt %u", rtp_hdr->v,
> + rtp_hdr->cc, rtp_hdr->pt);
> +
> + payload += sizeof(*rtp_hdr) + rtp_hdr->cc * 4 + 1;
> +
> + sbc_hdr = (struct sbc_frame_hdr *)payload;
> +
> + rtlbt_info("bitpool %u", sbc_hdr->bitpool);
> +
> + pr_sbc_hdr(sbc_hdr);
> +
> + bitpool = sbc_hdr->bitpool;
> + hci_cmd_send_to_fw(btpf, HCI_VENDOR_SET_BITPOOL_CMD, 1,
> + &bitpool);
This doesn't work, first of all not all codecs/media endpoits use RTP
to transmit the stream and this also assumes only SBC is supported.
Also the way this code checks packet length to detect whether this is
a stream channel sounds really bad, there is nothing is the A2DP spec
saying we need to fill at least 100 bytes, in fact Ive seen
implementations that send less frames to flow control the stream.
> + }
> + btpf->icount.a2dp++;
> +
> + }
> +
> + if (pf->idx == PROFILE_PAN)
> + btpf->icount.pan++;
> +
> +done:
> + return;
> +}
> +
> +static void hci_cmd_complete_evt(struct rtl_btpf *btpf, u8 total_len, u8 *p)
> +{
> + u16 opcode;
> + struct hci_ev_cmd_complete *cmdcp;
> +
> + cmdcp = (struct hci_ev_cmd_complete *)p;
> + opcode = le16_to_cpu(cmdcp->opcode);
> +
> + switch (opcode) {
> + case HCI_OP_READ_LOCAL_VERSION: {
> + struct hci_rp_read_local_version *v =
> + (struct hci_rp_read_local_version *)(p +
> + sizeof(*cmdcp));
> + if (v->status)
> + break;
> +
> + btpf->hci_rev = le16_to_cpu(v->hci_rev);
> + btpf->lmp_subver = le16_to_cpu(v->lmp_subver);
> + rtlbt_info("HCI Rev 0x%04x, LMP Subver 0x%04x", btpf->hci_rev,
> + btpf->lmp_subver);
> +
> + if (le16_to_cpu(v->manufacturer) == 0x005d) {
> + rtlbt_info("Realtek Semiconductor Corporation");
> + set_bit(BTPF_CID_RTL, &btpf->flags);
> + } else {
> + clear_bit(BTPF_CID_RTL, &btpf->flags);
> + }
> +
> + break;
> + }
> + default:
> + break;
> + }
> +}
> +
> +static void hci_conn_complete_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_conn_complete *ev = (void *)p;
> + u16 handle;
> + struct rtl_hci_conn *conn;
> +
> + handle = __le16_to_cpu(ev->handle);
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn) {
> + conn = kzalloc(sizeof(struct rtl_hci_conn), GFP_KERNEL);
> + if (conn) {
> + conn->handle = handle;
> + list_add_tail(&conn->list, &btpf->conn_list);
> + conn->pf_bits = 0;
> + memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
> + /* sco or esco */
> + if (ev->link_type == 0 || ev->link_type == 2) {
> + conn->type = SYNC_CONN;
> + profile_conn_get(btpf, conn, PROFILE_SCO);
> + } else {
> + conn->type = ACL_CONN;
> + }
> + } else {
> + rtlbt_err("%s: hci conn allocate fail.", __func__);
> + return;
> + }
> + } else {
> + /* If the connection has already existed, reset connection
> + * information
> + */
> + rtlbt_warn("%s: hci conn handle(0x%x) already existed",
> + __func__, handle);
> + conn->pf_bits = 0;
> + memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
> + /* sco or esco */
> + if (ev->link_type == 0 || ev->link_type == 2) {
> + conn->type = SYNC_CONN;
> + profile_conn_get(btpf, conn, PROFILE_SCO);
> + } else {
> + conn->type = ACL_CONN;
> + }
> + }
> +}
> +
> +static int hci_disconn_complete_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_disconn_complete *ev = (void *)p;
> + u16 handle;
> + struct rtl_hci_conn *conn;
> + struct list_head *pos, *temp;
> + struct rtl_profile *pf;
> +
> + handle = le16_to_cpu(ev->handle);
> +
> + rtlbt_info("%s: status %u, handle %04x, reason 0x%x", __func__,
> + ev->status, handle, ev->reason);
> +
> + if (ev->status)
> + return -1;
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn) {
> + rtlbt_err("hci conn handle(0x%x) not found", handle);
> + return -1;
> + }
> +
> + switch (conn->type) {
> + case ACL_CONN:
> + list_for_each_safe(pos, temp, &btpf->pf_list) {
> + pf = list_entry(pos, struct rtl_profile, list);
> + if (pf->handle == handle && pf->scid && pf->dcid) {
> + rtlbt_info(
> + "%s: hndl %04x psm %04x dcid %04x scid %04x",
> + __func__, pf->handle, pf->psm, pf->dcid,
> + pf->scid);
> + /* If both scid and dcid are bigger than zero,
> + * L2cap connection exists.
> + */
> + profile_conn_put(btpf, conn, pf->idx);
> + list_del(&pf->list);
> + kfree(pf);
> + }
> + }
> + break;
> +
> + case SYNC_CONN:
> + profile_conn_put(btpf, conn, PROFILE_SCO);
> + break;
> +
> + case LE_CONN:
> + profile_conn_put(btpf, conn, PROFILE_HID);
> + break;
> +
> + default:
> + break;
> + }
> +
> + list_del(&conn->list);
> + kfree(conn);
> +
> + return 0;
> +}
> +
> +static void hci_mode_change_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_mode_change *ev = (void *)p;
> +
> + hid_state_update(btpf, le16_to_cpu(ev->handle),
> + le16_to_cpu(ev->interval));
> +}
> +
> +static void rtl_le_conn_compl_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_le_conn_complete *ev = (void *)p;
> + u16 handle, interval;
> + struct rtl_hci_conn *conn;
> +
> + handle = le16_to_cpu(ev->handle);
> + interval = le16_to_cpu(ev->interval);
> +
> + conn = rtl_hci_conn_lookup(btpf, handle);
> + if (!conn) {
> + conn = kzalloc(sizeof(struct rtl_hci_conn), GFP_ATOMIC);
> + if (conn) {
> + conn->handle = handle;
> + list_add_tail(&conn->list, &btpf->conn_list);
> + conn->pf_bits = 0;
> + memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
> + conn->type = LE_CONN;
> + /* We consider le is the same as hid */
> + profile_conn_get(btpf, conn, PROFILE_HID);
> + hid_state_update(btpf, handle, interval);
> + } else {
> + rtlbt_err("%s: hci conn allocate fail.", __func__);
> + }
> + } else {
> + rtlbt_warn("%s: hci conn handle(%x) already existed.", __func__,
> + handle);
> + conn->pf_bits = 0;
> + memset(conn->pf_refs, 0, MAX_PROFILE_NUM);
> + conn->type = LE_CONN;
> + profile_conn_get(btpf, conn, PROFILE_HID);
> + hid_state_update(btpf, handle, interval);
> + }
> +}
> +
> +static void hci_le_conn_complete_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_le_conn_update_complete *ev = (void *)p;
> + u16 handle, interval;
> +
> + handle = le16_to_cpu(ev->handle);
> + interval = le16_to_cpu(ev->interval);
> + hid_state_update(btpf, handle, interval);
> +}
> +
> +static void hci_le_meta_evt(struct rtl_btpf *btpf, u8 *p)
> +{
> + struct hci_ev_le_meta *le_ev = (void *)p;
> +
> + p += sizeof(struct hci_ev_le_meta);
> +
> + switch (le_ev->subevent) {
> + case HCI_EV_LE_CONN_COMPLETE:
> + rtl_le_conn_compl_evt(btpf, p);
> + break;
> +
> + case HCI_EV_LE_CONN_UPDATE_COMPLETE:
> + hci_le_conn_complete_evt(btpf, p);
> + break;
> +
> + default:
> + break;
> + }
> +}
> +
> +static void hci_process_evt(struct rtl_btpf *btpf, u8 *p, u16 len)
> +{
> + struct hci_event_hdr *hdr = (struct hci_event_hdr *)p;
> +
> + (void)&len;
> +
> + p += sizeof(struct hci_event_hdr);
> +
> + switch (hdr->evt) {
> + case HCI_EV_CMD_COMPLETE:
> + hci_cmd_complete_evt(btpf, hdr->plen, p);
> + break;
> + case HCI_EV_CONN_COMPLETE:
> + case HCI_EV_SYNC_CONN_COMPLETE:
> + hci_conn_complete_evt(btpf, p);
> + break;
> + case HCI_EV_DISCONN_COMPLETE:
> + hci_disconn_complete_evt(btpf, p);
> + break;
> + case HCI_EV_MODE_CHANGE:
> + hci_mode_change_evt(btpf, p);
> + break;
> + case HCI_EV_LE_META:
> + hci_le_meta_evt(btpf, p);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static const char l2_dir_str[][4] = {
> + "RX", "TX",
> +};
> +
> +static void l2_process_frame(struct rtl_btpf *btpf, u8 *data, u16 len,
> + u8 out)
> +{
> + u16 handle;
> + u16 flags;
> + u16 chann_id;
> + u16 psm, scid, dcid, result;
> + struct hci_acl_hdr *acl_hdr = (void *)data;
> + struct l2cap_cmd_hdr *cmd;
> + struct l2cap_hdr *hdr;
> + struct l2cap_conn_req *conn_req;
> + struct l2cap_conn_rsp *conn_rsp;
> + struct l2cap_disconn_req *disc_req;
> +
> + handle = __le16_to_cpu(acl_hdr->handle);
> + flags = hci_flags(handle);
> + handle = hci_handle(handle);
> +
> + if (flags == ACL_CONT)
> + return;
> +
> + data += sizeof(*acl_hdr);
> +
> + hdr = (void *)data;
> + chann_id = le16_to_cpu(hdr->cid);
> +
> + if (chann_id != 0x0001) {
> + if (btpf->pf_bits & BIT(PROFILE_A2DP) ||
> + btpf->pf_bits & BIT(PROFILE_PAN))
> + packet_increment(btpf, handle, chann_id,
> + le16_to_cpu(hdr->len), data + 4, out);
> + return;
> + }
> +
> + data += sizeof(*hdr);
> +
> + cmd = (void *)data;
> + data += sizeof(*cmd);
> +
> + switch (cmd->code) {
> + case L2CAP_CONN_REQ:
> + conn_req = (void *)data;
> + psm = le16_to_cpu(conn_req->psm);
> + scid = le16_to_cpu(conn_req->scid);
> + rtlbt_info(
> + "%s l2cap conn req: hndl %04x psm %04x scid %04x",
> + l2_dir_str[out], handle, psm, scid);
> + handle_l2cap_conn_req(btpf, handle, psm, scid, out);
> + break;
> +
> + case L2CAP_CONN_RSP:
> + conn_rsp = (void *)data;
> + dcid = le16_to_cpu(conn_rsp->dcid);
> + scid = le16_to_cpu(conn_rsp->scid);
> + result = le16_to_cpu(conn_rsp->result);
> + rtlbt_info(
> + "%s l2cap conn rsp: hndl %04x dcid %04x scid %04x res %x",
> + l2_dir_str[out], handle, dcid, scid, result);
> + handle_l2cap_conn_rsp(btpf, handle, dcid, scid, out, result);
> + break;
> +
> + case L2CAP_DISCONN_REQ:
> + disc_req = (void *)data;
> + dcid = le16_to_cpu(disc_req->dcid);
> + scid = le16_to_cpu(disc_req->scid);
> + rtlbt_info(
> + "%s l2cap disc req: hndl %04x dcid %04x scid %04x",
> + l2_dir_str[out], handle, dcid, scid);
> + handle_l2cap_disconn_req(btpf, handle, dcid, scid, out);
> + break;
> + case L2CAP_DISCONN_RSP:
> + break;
> + default:
> + rtlbt_dbg("undesired l2 command code 0x%02x", cmd->code);
> + break;
> + }
> +}
> +
> +static void btpf_process_frame(struct rtl_btpf *btpf, struct sk_buff *skb)
> +{
> + u8 pkt_type = skb->data[0];
> +
> + skb_pull(skb, 1);
> +
> + if (!test_bit(BTPF_CID_RTL, &btpf->flags)) {
> + if (pkt_type == HCI_EVENT_PKT) {
> + struct hci_event_hdr *hdr = (void *)skb->data;
> +
> + if (hdr->evt == HCI_EV_CMD_COMPLETE) {
> + skb_pull(skb, sizeof(*hdr));
> + hci_cmd_complete_evt(btpf, hdr->plen,
> + skb->data);
> + }
> + }
> + return;
> + }
> +
> + switch (pkt_type) {
> + case HCI_EVENT_PKT:
> + hci_process_evt(btpf, skb->data, skb->len);
> + break;
> + case HCI_ACLDATA_PKT:
> + if (bt_cb(skb)->incoming)
> + l2_process_frame(btpf, skb->data, skb->len, 0);
> + else
> + l2_process_frame(btpf, skb->data, skb->len, 1);
> + break;
> + default:
> + break;
> + }
> +}
> +
> +static void btpf_process_work(struct work_struct *work)
> +{
> + struct rtl_btpf *btpf;
> + struct sock *sk;
> + struct sk_buff *skb;
> +
> + btpf = container_of(work, struct rtl_btpf, hci_work);
> + sk = btpf->hci_sock->sk;
> +
> + /* Get data directly from socket receive queue without copying it. */
> + while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
> + skb_orphan(skb);
> + btpf_process_frame(btpf, skb);
> + kfree_skb(skb);
> + }
> +}
> +
> +static void btpf_raw_data_ready(struct sock *sk)
> +{
> + struct rtl_btpf *btpf;
> +
> + /* rtlbt_dbg("qlen %d", skb_queue_len(&sk->sk_receive_queue)); */
> +
> + btpf = sk->sk_user_data;
> + queue_work(btpf->workq, &btpf->hci_work);
> +}
> +
> +static void btpf_raw_error_report(struct sock *sk)
> +{
> +}
> +
> +static int btpf_open_socket(struct rtl_btpf *btpf)
> +{
> + int ret;
> + struct sockaddr_hci addr;
> + struct sock *sk;
> + struct hci_filter flt;
> +
> + ret = sock_create_kern(&init_net, PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI,
> + &btpf->hci_sock);
> + if (ret < 0) {
> + rtlbt_err("Create hci sock error %d", ret);
> + goto err_1;
> + }
> +
> + memset(&addr, 0, sizeof(addr));
> + addr.hci_family = AF_BLUETOOTH;
> + /* Assume Realtek BT controller index is 0. */
> + addr.hci_dev = 0;
> + addr.hci_channel = HCI_CHANNEL_RAW;
> + ret = kernel_bind(btpf->hci_sock, (struct sockaddr *)&addr,
> + sizeof(addr));
> + if (ret < 0) {
> + rtlbt_err("Bind hci sock error");
> + goto err_2;
> + }
> +
> + memset(&flt, 0, sizeof(flt));
> + /* flt.type_mask = 0; */
> + flt.type_mask = (1 << HCI_EVENT_PKT | 1 << HCI_ACLDATA_PKT);
> + flt.event_mask[0] = 0xffffffff;
> + flt.event_mask[1] = 0xffffffff;
> +
> + ret = kernel_setsockopt(btpf->hci_sock, SOL_HCI, HCI_FILTER,
> + (char *)&flt, sizeof(flt));
> + if (ret < 0) {
> + rtlbt_err("Set hci sock filter error %d", ret);
> + goto err_2;
> + }
> +
> + sk = btpf->hci_sock->sk;
> + sk->sk_user_data = btpf;
> + sk->sk_data_ready = btpf_raw_data_ready;
> + sk->sk_error_report = btpf_raw_error_report;
> +
> + set_bit(BTPF_HCI_SOCK, &btpf->flags);
> +
> + return 0;
> +err_2:
> + sock_release(btpf->hci_sock);
> +err_1:
> + return ret;
> +}
> +
> +static void btpf_close_socket(struct rtl_btpf *btpf)
> +{
> + struct socket *socket = btpf->hci_sock;
> +
> + if (socket) {
> + btpf->hci_sock = NULL;
> + kernel_sock_shutdown(socket, SHUT_RDWR);
> + socket->sk->sk_user_data = NULL;
> + sock_release(socket);
> + }
> +
> + clear_bit(BTPF_HCI_SOCK, &btpf->flags);
> +}
> +
> +int rtl_btpf_init(void)
> +{
> + int i;
> + struct rtl_btpf *btpf;
> + int ret = 0;
> +
> + btpf = kzalloc(sizeof(struct rtl_btpf), GFP_KERNEL);
> + if (!btpf)
> + return -ENOMEM;
> +
> + INIT_LIST_HEAD(&btpf->conn_list);
> + INIT_LIST_HEAD(&btpf->pf_list);
> +
> + btpf->pf_bits = 0;
> + btpf->pf_state = 0;
> + for (i = 0; i < MAX_PROFILE_NUM; i++)
> + btpf->pf_refs[i] = 0;
> +
> + INIT_WORK(&btpf->hci_work, btpf_process_work);
> +
> + btpf->workq = create_workqueue("rtl_btpf_workq");
> + if (!btpf->workq) {
> + ret = -ENOMEM;
> + goto err_1;
> + }
> +
> + /* init sock */
> + ret = btpf_open_socket(btpf);
> + if (ret < 0) {
> + rtlbt_err("Failed to open sock to monitor tx/rx");
> + goto err_2;
> + }
> +
> + rtl_btpf = btpf;
> +
> + rtlbt_info("rtl btpf initialized");
> +
> + return 0;
> +err_2:
> + flush_workqueue(btpf->workq);
> + destroy_workqueue(btpf->workq);
> +err_1:
> + kfree(btpf);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(rtl_btpf_init);
> +
> +void rtl_btpf_deinit(void)
> +{
> + struct rtl_btpf *btpf = rtl_btpf;
> +
> + rtlbt_info("rtl btpf de-initialize");
> +
> + rtl_btpf = NULL;
> +
> + if (!btpf)
> + return;
> +
> + flush_workqueue(btpf->workq);
> + destroy_workqueue(btpf->workq);
> +
> + del_timer_sync(&btpf->a2dp_timer);
> + del_timer_sync(&btpf->pan_timer);
> +
> + rtl_hci_conn_list_purge(btpf);
> + rtl_profile_list_purge(btpf);
> +
> + btpf_close_socket(btpf);
> +
> + kfree(btpf);
> +}
> +EXPORT_SYMBOL_GPL(rtl_btpf_deinit);
> +
> +MODULE_AUTHOR("Alex Lu <[email protected]>");
> +MODULE_DESCRIPTION("Bluetooth profiling for Realtek devices ver " VERSION);
> +MODULE_VERSION(VERSION);
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/bluetooth/rtl_btpf.h b/drivers/bluetooth/rtl_btpf.h
> new file mode 100644
> index 0000000..2d507b0
> --- /dev/null
> +++ b/drivers/bluetooth/rtl_btpf.h
> @@ -0,0 +1,184 @@
> +/*
> + *
> + * Realtek Bluetooth Profile profiling driver
> + *
> + * Copyright (C) 2015 Realtek Semiconductor Corporation
> + *
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <net/bluetooth/hci_core.h>
> +#include <linux/list.h>
> +
> +#define rtlbt_dbg(fmt, ...) \
> + pr_debug("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
> +#define rtlbt_info(fmt, ...) \
> + pr_info("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
> +#define rtlbt_warn(fmt, ...) \
> + pr_warn("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
> +#define rtlbt_err(fmt, ...) \
> + pr_err("rtl_btpf: " fmt "\n", ##__VA_ARGS__)
> +
> +#define HCI_VENDOR_SET_PF_REPORT_CMD 0xfc19
> +#define HCI_VENDOR_SET_BITPOOL_CMD 0xfc51
> +
> +#define PAN_PACKET_COUNT 5
> +
> +#define ACL_CONN 0x0
> +#define SYNC_CONN 0x1
> +#define LE_CONN 0x2
> +
> +#define PSM_SDP 0x0001
> +#define PSM_RFCOMM 0x0003
> +#define PSM_PAN 0x000F
> +#define PSM_HID 0x0011
> +#define PSM_HID_INT 0x0013
> +#define PSM_AVCTP 0x0017
> +#define PSM_AVDTP 0x0019
> +#define PSM_FTP 0x1001
> +#define PSM_BIP 0x1003
> +#define PSM_OPP 0x1015
> +
> +#define MAX_PROFILE_NUM 7
> +enum __profile_type {
> + PROFILE_SCO = 0,
> + PROFILE_HID = 1,
> + PROFILE_A2DP = 2,
> + PROFILE_PAN = 3,
> + PROFILE_HID2 = 4, /* hid interval */
> + PROFILE_HOGP = 5,
> + PROFILE_VOICE = 6,
> + PROFILE_MAX = 7
> +};
> +
> +struct pf_pkt_icount {
> + u32 a2dp;
> + u32 pan;
> + u32 hogp;
> + u32 voice;
> +};
> +
> +#define RTL_FROM_REMOTE 0
> +#define RTL_TO_REMOTE 1
> +
> +#define RTL_PROFILE_MATCH_HANDLE (1 << 0)
> +#define RTL_PROFILE_MATCH_SCID (1 << 1)
> +#define RTL_PROFILE_MATCH_DCID (1 << 2)
> +struct rtl_profile_id {
> + u16 match_flags;
> + u16 handle;
> + u16 dcid;
> + u16 scid;
> +};
> +
> +struct rtl_profile {
> + struct list_head list;
> + u16 handle;
> + u16 psm;
> + u16 dcid;
> + u16 scid;
> + u8 idx;
> +};
> +
> +struct rtl_hci_conn {
> + struct list_head list;
> + u16 handle;
> + u8 type;
> + u8 pf_bits;
> + int pf_refs[MAX_PROFILE_NUM];
> +};
> +
> +struct rtl_btpf {
> + u16 hci_rev;
> + u16 lmp_subver;
> +
> + struct hci_dev *hdev;
> + struct list_head pf_list;
> + struct list_head conn_list;
> +
> + u8 pf_bits;
> + u8 pf_state;
> + int pf_refs[MAX_PROFILE_NUM];
> +
> + struct pf_pkt_icount icount;
> +
> + /* Monitor timers */
> + struct timer_list a2dp_timer;
> + struct timer_list pan_timer;
> +
> + struct workqueue_struct *workq;
> + struct work_struct hci_work;
> +
> + struct socket *hci_sock;
> +#define BTPF_HCI_SOCK 1
> +#define BTPF_CID_RTL 2
> + unsigned long flags;
> +};
> +
> +#ifdef __LITTLE_ENDIAN
> +struct sbc_frame_hdr {
> + u8 syncword:8; /* Sync word */
> + u8 subbands:1; /* Subbands */
> + u8 allocation_method:1; /* Allocation method */
> + u8 channel_mode:2; /* Channel mode */
> + u8 blocks:2; /* Blocks */
> + u8 sampling_frequency:2; /* Sampling frequency */
> + u8 bitpool:8; /* Bitpool */
> + u8 crc_check:8; /* CRC check */
> +} __packed;
> +
> +struct rtp_header {
> + unsigned cc:4;
> + unsigned x:1;
> + unsigned p:1;
> + unsigned v:2;
> +
> + unsigned pt:7;
> + unsigned m:1;
> +
> + u16 sequence_number;
> + u32 timestamp;
> + u32 ssrc;
> + u32 csrc[0];
> +} __packed;
> +
> +#else /* !__LITTLE_ENDIAN */
> +struct sbc_frame_hdr {
> + u8 syncword:8; /* Sync word */
> + u8 sampling_frequency:2; /* Sampling frequency */
> + u8 blocks:2; /* Blocks */
> + u8 channel_mode:2; /* Channel mode */
> + u8 allocation_method:1; /* Allocation method */
> + u8 subbands:1; /* Subbands */
> + u8 bitpool:8; /* Bitpool */
> + u8 crc_check:8; /* CRC check */
> +} __packed;
> +
> +struct rtp_header {
> + unsigned v:2;
> + unsigned p:1;
> + unsigned x:1;
> + unsigned cc:4;
> +
> + unsigned m:1;
> + unsigned pt:7;
> +
> + u16 sequence_number;
> + u32 timestamp;
> + u32 ssrc;
> + u32 csrc[0];
> +} __packed;
> +#endif /* __LITTLE_ENDIAN */
> +
> +void rtl_btpf_deinit(void);
> +int rtl_btpf_init(void);
> --
> 2.10.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Luiz Augusto von Dentz