From: Alex Lu <alex...@realsil.com.cn>

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...@realsil.com.cn>
Signed-off-by: Larry Finger <larry.fin...@lwfinger.net>
---
 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...@realsil.com.cn>");
+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

Reply via email to