[PATCH] rtlbt: Add Realtek Bluetooth profiling support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Alex Lu <alex_lu@xxxxxxxxxxxxxx>

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 <alex_lu@xxxxxxxxxxxxxx>
Signed-off-by: Larry Finger <Larry.Finger@xxxxxxxxxxxx>
---
 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 <alex_lu@xxxxxxxxxxxxxx>");
+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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux