realtek:bt Add new bt-usb driver release package.

1. Update to VERSION "3.1.6fd4e69.20220818-105856"
2. Update the fw file for RTL8851A, RTL8852B
3. rtk_coex: Support vendor cmd for reporting the profile and state of each connection
4. rtk_bt/rtk_misc/rtk_coex/hci_ldisc/btrtksdio: fix the issues by coverity scan and Sparse build
5. rtk_bt: Add shutdown wakeup and fix failure of usb enumeration
6. rtk_bt: Add marco to distinguish powerkey or anykey wakeup

Bug 3528414

Change-Id: Ia44029f189f8fc2cd62160dbc763c6e91e8cd9b4
Signed-off-by: Sushil Singh <sushilkumars@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2876940
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
Tested-by: Revanth Kumar Uppala <ruppala@nvidia.com>
Reviewed-by: Revanth Kumar Uppala <ruppala@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
This commit is contained in:
Sushil Kumar Singh
2023-01-19 22:25:01 +05:30
committed by mobile promotions
parent 6e2e6973cd
commit 5bc80fd7c9
7 changed files with 772 additions and 310 deletions

View File

@@ -1,5 +1,21 @@
# SPDX-License-Identifier: GPL-2.0
CONFIG_BTUSB_AUTOSUSPEND = n
CONFIG_BTUSB_WAKEUP_HOST = n
CONFIG_BTCOEX = y
ifeq ($(CONFIG_BTUSB_AUTOSUSPEND), y)
EXTRA_CFLAGS += -DCONFIG_BTUSB_AUTOSUSPEND
endif
ifeq ($(CONFIG_BTUSB_WAKEUP_HOST), y)
EXTRA_CFLAGS += -DCONFIG_BTUSB_WAKEUP_HOST
endif
ifeq ($(CONFIG_BTCOEX), y)
EXTRA_CFLAGS += -DCONFIG_BTCOEX
endif
obj-m := rtk_btusb.o
rtk_btusb-objs := rtk_coex.o \
rtk_misc.o \

View File

@@ -15,13 +15,14 @@
#include <linux/skbuff.h>
#include <linux/usb.h>
#include <linux/dcache.h>
#include <linux/reboot.h>
#include <net/sock.h>
#include <asm/unaligned.h>
#include "rtk_bt.h"
#include "rtk_misc.h"
#define VERSION "3.1.f363121.20220505-144113"
#define VERSION "3.1.6fd4e69.20220818-105856"
#ifdef BTCOEX
#include "rtk_coex.h"
@@ -30,7 +31,7 @@
#ifdef RTKBT_SWITCH_PATCH
#include <linux/semaphore.h>
#include <net/bluetooth/hci_core.h>
DEFINE_SEMAPHORE(switch_sem);
static DEFINE_SEMAPHORE(switch_sem);
#endif
#if HCI_VERSION_CODE >= KERNEL_VERSION(3, 7, 1)
@@ -774,7 +775,10 @@ static int btusb_open(struct hci_dev *hdev)
goto failed;
/*******************************/
RTKBT_INFO("%s set HCI_RUNNING", __func__);
RTKBT_INFO("%s set HCI UP RUNNING", __func__);
if (test_and_set_bit(HCI_UP, &hdev->flags))
goto done;
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
goto done;
@@ -912,7 +916,7 @@ static int btusb_flush(struct hci_dev *hdev)
return 0;
}
const char pkt_ind[][8] = {
static const char pkt_ind[][8] = {
[HCI_COMMAND_PKT] = "cmd",
[HCI_ACLDATA_PKT] = "acl",
[HCI_SCODATA_PKT] = "sco",
@@ -1329,7 +1333,7 @@ static int rtkbt_lookup_le_device_poweron_whitelist(struct hci_dev *hdev,
}
#endif
int rtkbt_pm_notify(struct notifier_block *notifier,
static int rtkbt_pm_notify(struct notifier_block *notifier,
ulong pm_event, void *unused)
{
struct btusb_data *data;
@@ -1418,13 +1422,6 @@ int rtkbt_pm_notify(struct notifier_block *notifier,
kfree(cmd);
msleep(100); /* From FW colleague's recommendation */
result = download_lps_patch(intf);
/* Tell the controller to wake up host if received special
* advertising packet
*/
set_scan(intf);
/* Send special vendor commands */
#endif
#ifdef RTKBT_TV_POWERON_WHITELIST
@@ -1433,6 +1430,17 @@ int rtkbt_pm_notify(struct notifier_block *notifier,
RTKBT_ERR("rtkbt_lookup_le_device_poweron_whitelist error: %d", result);
}
#endif
#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SWITCH_PATCH
#ifdef RTKBT_POWERKEY_WAKEUP
/* Tell the controller to wake up host if received special
* advertising packet
*/
set_scan(intf);
#endif
/* Send special vendor commands */
#endif
break;
case PM_POST_SUSPEND:
@@ -1466,7 +1474,7 @@ int rtkbt_pm_notify(struct notifier_block *notifier,
}
#endif
#if BTUSB_RPM
#ifdef BTUSB_RPM
RTKBT_DBG("%s: Re-enable autosuspend", __func__);
/* pm_runtime_use_autosuspend(&udev->dev);
* pm_runtime_set_autosuspend_delay(&udev->dev, 2000);
@@ -1491,6 +1499,36 @@ int rtkbt_pm_notify(struct notifier_block *notifier,
return NOTIFY_DONE;
}
static int rtkbt_shutdown_notify(struct notifier_block *notifier,
ulong pm_event, void *unused)
{
struct btusb_data *data;
struct usb_device *udev;
struct usb_interface *intf;
struct hci_dev *hdev;
/* int err; */
data = container_of(notifier, struct btusb_data, shutdown_notifier);
udev = data->udev;
intf = data->intf;
hdev = data->hdev;
RTKBT_DBG("%s: pm_event %ld", __func__, pm_event);
switch (pm_event) {
case SYS_POWER_OFF:
case SYS_RESTART:
#ifdef RTKBT_SHUTDOWN_WAKEUP
RTKBT_DBG("%s: power off", __func__);
set_scan(intf);
#endif
break;
default:
break;
}
return NOTIFY_DONE;
}
static int btusb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
@@ -1513,7 +1551,7 @@ static int btusb_probe(struct usb_interface *intf,
flag1 = device_can_wakeup(&udev->dev);
flag2 = device_may_wakeup(&udev->dev);
RTKBT_DBG("btusb_probe can_wakeup %x, may wakeup %x", flag1, flag2);
#if BTUSB_WAKEUP_HOST
#ifdef BTUSB_WAKEUP_HOST
device_wakeup_enable(&udev->dev);
#endif
//device_wakeup_enable(&udev->dev);
@@ -1533,6 +1571,10 @@ static int btusb_probe(struct usb_interface *intf,
for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
ep_desc = &intf->cur_altsetting->endpoint[i].desc;
if (!data->intr_ep && usb_endpoint_is_bulk_in(ep_desc) && (ep_desc->bEndpointAddress == 0x81)) {
data->intr_ep = ep_desc;
continue;
}
if (!data->intr_ep && usb_endpoint_is_int_in(ep_desc)) {
data->intr_ep = ep_desc;
@@ -1639,6 +1681,9 @@ static int btusb_probe(struct usb_interface *intf,
data->pm_notifier.notifier_call = rtkbt_pm_notify;
register_pm_notifier(&data->pm_notifier);
/* Register POWER-OFF notifier */
data->shutdown_notifier.notifier_call = rtkbt_shutdown_notify;
register_reboot_notifier(&data->shutdown_notifier);
#ifdef BTCOEX
rtk_btcoex_probe(hdev);
#endif
@@ -1665,6 +1710,7 @@ static void btusb_disconnect(struct usb_interface *intf)
/* Un-register PM notifier */
unregister_pm_notifier(&data->pm_notifier);
unregister_reboot_notifier(&data->shutdown_notifier);
/*******************************/
patch_remove(intf);
@@ -1846,7 +1892,8 @@ static struct usb_driver btusb_driver = {
#ifdef CONFIG_PM
.suspend = btusb_suspend,
.resume = btusb_resume,
#ifdef RTKBT_SWITCH_PATCH
#if defined RTKBT_SWITCH_PATCH || defined RTKBT_SUSPEND_WAKEUP || defined \
RTKBT_SHUTDOWN_WAKEUP
.reset_resume = btusb_resume,
#endif
#endif

View File

@@ -30,12 +30,16 @@
/* #define HCI_VERSION_CODE KERNEL_VERSION(3, 14, 41) */
#define HCI_VERSION_CODE LINUX_VERSION_CODE
#ifdef CONFIG_BTCOEX
#define BTCOEX
#endif
/***********************************
** Realtek - For rtk_btusb driver **
***********************************/
#define BTUSB_WAKEUP_HOST 0 /* 1 enable; 0 disable */
#ifdef CONFIG_BTUSB_WAKEUP_HOST
#define BTUSB_WAKEUP_HOST
#endif
#define URB_CANCELING_DELAY_MS 10 // Added by Realtek
#if HCI_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
@@ -44,12 +48,6 @@
#define HDEV_BUS hdev->type
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
#define USB_RPM 1
#else
#define USB_RPM 0
#endif
#if HCI_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
#define NUM_REASSEMBLY 3
#endif
@@ -135,6 +133,7 @@ struct btusb_data {
int (*recv_bulk) (struct btusb_data * data, void *buffer, int count);
#endif
struct notifier_block pm_notifier;
struct notifier_block shutdown_notifier;
void *context;
};

View File

@@ -60,12 +60,16 @@ static u8 rtw_coex_on;
#endif
#define is_profile_connected(profile) ((btrtl_coex.profile_bitmap & BIT(profile)) > 0)
#define is_profile_busy(profile) ((btrtl_coex.profile_status & BIT(profile)) > 0)
#define is_profile_busy(conn, profile) ((conn->profile_status & BIT(profile)) > 0)
#ifdef RTB_SOFTWARE_MAILBOX
static void rtk_handle_event_from_wifi(uint8_t * msg);
#endif
static void count_a2dp_packet_timeout(struct work_struct *work);
static void count_pan_packet_timeout(struct work_struct *work);
static void count_hogp_packet_timeout(struct work_struct *work);
static int rtl_alloc_buff(struct rtl_coex_struct *coex)
{
struct rtl_hci_ev *ev;
@@ -226,12 +230,12 @@ static int rtl_l2_node_to_used(struct rtl_coex_struct *coex,
return 0;
}
static int8_t psm_to_profile_index(uint16_t psm)
static uint8_t psm_to_profile_index(uint16_t psm)
{
switch (psm) {
case PSM_AVCTP:
case PSM_SDP:
return -1; //ignore
return 0xFF; //ignore
case PSM_HID:
case PSM_HID_INT:
@@ -252,7 +256,7 @@ static int8_t psm_to_profile_index(uint16_t psm)
}
}
static rtk_prof_info *find_by_psm(u16 psm)
static rtk_prof_info *find_by_psm(u16 handle, u16 psm)
{
struct list_head *head = &btrtl_coex.profile_list;
struct list_head *iter = NULL;
@@ -261,65 +265,59 @@ static rtk_prof_info *find_by_psm(u16 psm)
list_for_each_safe(iter, temp, head) {
desc = list_entry(iter, rtk_prof_info, list);
if (desc->psm == psm)
if ((handle & 0xfff) == (desc->handle & 0xfff) &&
desc->psm == psm)
return desc;
}
return NULL;
}
static void rtk_check_setup_timer(int8_t profile_index)
static void rtk_check_setup_timer(rtk_conn_prof * phci_conn, uint8_t profile_index)
{
int delay = msecs_to_jiffies(1000);
if (profile_index == profile_a2dp) {
btrtl_coex.a2dp_packet_count = 0;
btrtl_coex.a2dp_count_timer.expires =
jiffies + msecs_to_jiffies(1000);
mod_timer(&btrtl_coex.a2dp_count_timer,
btrtl_coex.a2dp_count_timer.expires);
phci_conn->a2dp_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->a2dp_count_work, delay);
}
if (profile_index == profile_pan) {
btrtl_coex.pan_packet_count = 0;
btrtl_coex.pan_count_timer.expires =
jiffies + msecs_to_jiffies(1000);
mod_timer(&btrtl_coex.pan_count_timer,
btrtl_coex.pan_count_timer.expires);
phci_conn->pan_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->pan_count_work, delay);
}
/* hogp & voice share one timer now */
if ((profile_index == profile_hogp) || (profile_index == profile_voice)) {
if ((0 == btrtl_coex.profile_refcount[profile_hogp])
&& (0 == btrtl_coex.profile_refcount[profile_voice])) {
btrtl_coex.hogp_packet_count = 0;
btrtl_coex.voice_packet_count = 0;
btrtl_coex.hogp_count_timer.expires =
jiffies + msecs_to_jiffies(1000);
mod_timer(&btrtl_coex.hogp_count_timer,
btrtl_coex.hogp_count_timer.expires);
phci_conn->hogp_packet_count = 0;
phci_conn->voice_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &phci_conn->hogp_count_work, delay);
}
}
}
static void rtk_check_del_timer(int8_t profile_index)
static void rtk_check_del_timer(uint8_t profile_index, rtk_conn_prof * phci_conn)
{
RTKBT_DBG("%s: handle 0x%4x", __func__, phci_conn->handle);
if (profile_a2dp == profile_index) {
btrtl_coex.a2dp_packet_count = 0;
del_timer_sync(&btrtl_coex.a2dp_count_timer);
phci_conn->a2dp_packet_count = 0;
cancel_delayed_work_sync(&phci_conn->a2dp_count_work);
}
if (profile_pan == profile_index) {
btrtl_coex.pan_packet_count = 0;
del_timer_sync(&btrtl_coex.pan_count_timer);
phci_conn->pan_packet_count = 0;
cancel_delayed_work_sync(&phci_conn->pan_count_work);
}
if (profile_hogp == profile_index) {
btrtl_coex.hogp_packet_count = 0;
phci_conn->hogp_packet_count = 0;
if (btrtl_coex.profile_refcount[profile_voice] == 0) {
del_timer_sync(&btrtl_coex.hogp_count_timer);
cancel_delayed_work_sync(&phci_conn->hogp_count_work);
}
}
if (profile_voice == profile_index) {
btrtl_coex.voice_packet_count = 0;
phci_conn->voice_packet_count = 0;
if (btrtl_coex.profile_refcount[profile_hogp] == 0) {
del_timer_sync(&btrtl_coex.hogp_count_timer);
cancel_delayed_work_sync(&phci_conn->hogp_count_work);
}
}
}
@@ -335,7 +333,7 @@ static rtk_conn_prof *find_connection_by_handle(struct rtl_coex_struct * coex,
list_for_each_safe(iter, temp, head) {
desc = list_entry(iter, rtk_conn_prof, list);
if ((handle & 0xEFF) == desc->handle) {
if ((handle & 0xFFF) == desc->handle) {
return desc;
}
}
@@ -363,11 +361,17 @@ static void add_connection_to_hash(struct rtl_coex_struct * coex,
{
struct list_head *head = &coex->conn_hash;
list_add_tail(&desc->list, head);
INIT_DELAYED_WORK(&desc->a2dp_count_work, (void *)count_a2dp_packet_timeout);
INIT_DELAYED_WORK(&desc->pan_count_work, (void *)count_pan_packet_timeout);
INIT_DELAYED_WORK(&desc->hogp_count_work, (void *)count_hogp_packet_timeout);
}
static void delete_connection_from_hash(rtk_conn_prof * desc)
{
if (desc) {
cancel_delayed_work_sync(&desc->a2dp_count_work);
cancel_delayed_work_sync(&desc->pan_count_work);
cancel_delayed_work_sync(&desc->hogp_count_work);
list_del(&desc->list);
kfree(desc);
}
@@ -382,6 +386,9 @@ static void flush_connection_hash(struct rtl_coex_struct * coex)
list_for_each_safe(iter, temp, head) {
desc = list_entry(iter, rtk_conn_prof, list);
if (desc) {
cancel_delayed_work_sync(&desc->a2dp_count_work);
cancel_delayed_work_sync(&desc->pan_count_work);
cancel_delayed_work_sync(&desc->hogp_count_work);
list_del(&desc->list);
kfree(desc);
}
@@ -396,16 +403,11 @@ static void init_profile_hash(struct rtl_coex_struct * coex)
}
static uint8_t list_allocate_add(uint16_t handle, uint16_t psm,
int8_t profile_index, uint16_t dcid,
uint8_t profile_index, uint16_t dcid,
uint16_t scid)
{
rtk_prof_info *pprof_info = NULL;
if (profile_index < 0) {
RTKBT_ERR("PSM 0x%x do not need parse", psm);
return FALSE;
}
pprof_info = kmalloc(sizeof(rtk_prof_info), GFP_ATOMIC);
if (NULL == pprof_info) {
@@ -417,7 +419,7 @@ static uint8_t list_allocate_add(uint16_t handle, uint16_t psm,
* a2dp signal channel will be created first than media channel.
*/
if (psm == PSM_AVDTP) {
rtk_prof_info *pinfo = find_by_psm(psm);
rtk_prof_info *pinfo = find_by_psm(handle, psm);
if (!pinfo) {
pprof_info->flags = A2DP_SIGNAL;
RTKBT_INFO("%s: Add a2dp signal channel", __func__);
@@ -459,7 +461,15 @@ static void flush_profile_hash(struct rtl_coex_struct * coex)
spin_lock(&btrtl_coex.spin_lock_profile);
list_for_each_safe(iter, temp, head) {
desc = list_entry(iter, rtk_prof_info, list);
delete_profile_from_hash(desc);
if (desc) {
RTKBT_DBG("Delete profile: hndl 0x%04x, psm 0x%04x, "
"dcid 0x%04x, scid 0x%04x", desc->handle,
desc->psm, desc->dcid, desc->scid);
list_del(&desc->list);
kfree(desc);
desc = NULL;
}
}
//INIT_LIST_HEAD(head);
spin_unlock(&btrtl_coex.spin_lock_profile);
@@ -594,6 +604,7 @@ static void rtk_vendor_cmd_to_fw(uint16_t opcode, uint8_t parameter_len,
return;
}
static uint8_t profileinfo_cmd = 0;
static void rtk_notify_profileinfo_to_fw(void)
{
struct list_head *head = NULL;
@@ -612,7 +623,11 @@ static void rtk_notify_profileinfo_to_fw(void)
handle_number++;
}
buffer_size = 1 + handle_number * 3 + 1;
if(!profileinfo_cmd) {
buffer_size = 1 + handle_number * 3 + 1;
} else {
buffer_size = 1 + handle_number * 6;
}
p_buf = kmalloc(buffer_size, GFP_ATOMIC);
@@ -621,41 +636,55 @@ static void rtk_notify_profileinfo_to_fw(void)
return;
}
p = p_buf;
*p++ = handle_number;
RTKBT_DBG("%s: BufferSize %u", __func__, buffer_size);
*p++ = handle_number;
RTKBT_DBG("%s: NumberOfHandles %u", __func__, handle_number);
head = &btrtl_coex.conn_hash;
list_for_each(iter, head) {
hci_conn = list_entry(iter, rtk_conn_prof, list);
if (hci_conn && hci_conn->profile_bitmap) {
UINT16_TO_STREAM(p, hci_conn->handle);
RTKBT_DBG("%s: handle 0x%04x", __func__,
hci_conn->handle);
*p++ = hci_conn->profile_bitmap;
RTKBT_DBG("%s: profile_bitmap 0x%02x", __func__,
hci_conn->profile_bitmap);
if(!profileinfo_cmd) {
UINT16_TO_STREAM(p, hci_conn->handle);
RTKBT_DBG("%s: handle 0x%04x", __func__,
hci_conn->handle);
*p++ = hci_conn->profile_bitmap;
} else {
UINT16_TO_STREAM(p, hci_conn->handle);
UINT16_TO_STREAM(p, hci_conn->profile_bitmap);
RTKBT_DBG("%s: profile_bitmap 0x%02x", __func__,
hci_conn->profile_bitmap);
UINT16_TO_STREAM(p, hci_conn->profile_status);
RTKBT_DBG("%s: profile_status 0x%02x", __func__,
hci_conn->profile_status);
}
handle_number--;
}
if (0 == handle_number)
break;
}
*p++ = btrtl_coex.profile_status;
RTKBT_DBG("%s: profile_status 0x%02x", __func__,
btrtl_coex.profile_status);
rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_COMMAND, buffer_size,
if(!profileinfo_cmd) {
*p++ = btrtl_coex.profile_status;
rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND, buffer_size,
p_buf);
} else {
rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_COMMAND, buffer_size,
p_buf);
}
kfree(p_buf);
return;
}
static void update_profile_state(uint8_t profile_index, uint8_t is_busy)
static void update_profile_state(rtk_conn_prof * phci_conn,
uint8_t profile_index, uint8_t is_busy)
{
uint8_t need_update = FALSE;
RTKBT_DBG("%s: is_busy %d, profile_index %x", __func__,
is_busy, profile_index);
if ((btrtl_coex.profile_bitmap & BIT(profile_index)) == 0) {
RTKBT_ERR("%s: : ERROR!!! profile(Index: %x) does not exist",
__func__, profile_index);
@@ -663,36 +692,34 @@ static void update_profile_state(uint8_t profile_index, uint8_t is_busy)
}
if (is_busy) {
if ((btrtl_coex.profile_status & BIT(profile_index)) == 0) {
if ((phci_conn->profile_status & BIT(profile_index)) == 0) {
need_update = TRUE;
btrtl_coex.profile_status |= BIT(profile_index);
phci_conn->profile_status |= BIT(profile_index);
}
} else {
if ((btrtl_coex.profile_status & BIT(profile_index)) > 0) {
if ((phci_conn->profile_status & BIT(profile_index)) > 0) {
need_update = TRUE;
btrtl_coex.profile_status &= ~(BIT(profile_index));
phci_conn->profile_status &= ~(BIT(profile_index));
}
}
if (need_update) {
RTKBT_DBG("%s: btrtl_coex.profie_bitmap = %x",
__func__, btrtl_coex.profile_bitmap);
RTKBT_DBG("%s: btrtl_coex.profile_status = %x",
__func__, btrtl_coex.profile_status);
RTKBT_DBG("%s: btrtl_coex.profile_status 0x%02x, phci_conn->profile_status 0x%02x",
__func__, btrtl_coex.profile_status, phci_conn->profile_status);
rtk_notify_profileinfo_to_fw();
}
}
static void update_profile_connection(rtk_conn_prof * phci_conn,
int8_t profile_index, uint8_t is_add)
uint8_t profile_index, uint8_t is_add)
{
uint8_t need_update = FALSE;
uint8_t kk;
RTKBT_DBG("%s: is_add %d, profile_index %x", __func__,
is_add, profile_index);
if (profile_index < 0)
return;
if (is_add) {
if (btrtl_coex.profile_refcount[profile_index] == 0) {
@@ -703,14 +730,13 @@ static void update_profile_connection(rtk_conn_prof * phci_conn,
if (profile_index == profile_sco)
btrtl_coex.profile_status |=
BIT(profile_index);
rtk_check_setup_timer(profile_index);
}
btrtl_coex.profile_refcount[profile_index]++;
if (0 == phci_conn->profile_refcount[profile_index]) {
need_update = TRUE;
phci_conn->profile_bitmap |= BIT(profile_index);
rtk_check_setup_timer(phci_conn, profile_index);
}
phci_conn->profile_refcount[profile_index]++;
} else {
@@ -729,7 +755,6 @@ static void update_profile_connection(rtk_conn_prof * phci_conn,
/* if profile does not exist, status is meaningless */
btrtl_coex.profile_status &= ~(BIT(profile_index));
rtk_check_del_timer(profile_index);
}
phci_conn->profile_refcount[profile_index]--;
@@ -737,20 +762,22 @@ static void update_profile_connection(rtk_conn_prof * phci_conn,
need_update = TRUE;
phci_conn->profile_bitmap &= ~(BIT(profile_index));
phci_conn->profile_status &= ~(BIT(profile_index));
rtk_check_del_timer(profile_index, phci_conn);
/* clear profile_hid_interval if need */
if ((profile_hid == profile_index)
&& (phci_conn->
profile_bitmap & (BIT(profile_hid_interval)))) {
phci_conn->profile_bitmap &=
~(BIT(profile_hid_interval));
btrtl_coex.
phci_conn->
profile_refcount[profile_hid_interval]--;
}
}
}
RTKBT_DBG("%s: btrtl_coex.profile_bitmap 0x%02x", __func__,
btrtl_coex.profile_bitmap);
RTKBT_DBG("%s: phci_conn->profile_bitmap 0x%02x", __func__,
phci_conn->profile_bitmap);
for (kk = 0; kk < 8; kk++)
RTKBT_DBG("%s: btrtl_coex.profile_refcount[%d] = %d",
__func__, kk,
@@ -781,10 +808,10 @@ static void update_hid_active_state(uint16_t handle, uint16_t interval)
need_update = 1;
phci_conn->profile_bitmap |= BIT(profile_hid_interval);
btrtl_coex.profile_refcount[profile_hid_interval]++;
if (btrtl_coex.
phci_conn->profile_refcount[profile_hid_interval]++;
if (phci_conn->
profile_refcount[profile_hid_interval] == 1)
btrtl_coex.profile_status |=
phci_conn->profile_status |=
BIT(profile_hid);
}
} else {
@@ -793,10 +820,10 @@ static void update_hid_active_state(uint16_t handle, uint16_t interval)
phci_conn->profile_bitmap &=
~(BIT(profile_hid_interval));
btrtl_coex.profile_refcount[profile_hid_interval]--;
if (btrtl_coex.
phci_conn->profile_refcount[profile_hid_interval]--;
if (phci_conn->
profile_refcount[profile_hid_interval] == 0)
btrtl_coex.profile_status &=
phci_conn->profile_status &=
~(BIT(profile_hid));
}
}
@@ -810,9 +837,9 @@ static uint8_t handle_l2cap_con_req(uint16_t handle, uint16_t psm,
{
uint8_t status = FALSE;
rtk_prof_info *prof_info = NULL;
int8_t profile_index = psm_to_profile_index(psm);
uint8_t profile_index = psm_to_profile_index(psm);
if (profile_index < 0) {
if (profile_index == 0xFF) {
RTKBT_DBG("PSM(0x%04x) do not need parse", psm);
return status;
}
@@ -940,7 +967,7 @@ static const char alloc_methods[2][12] = {
static const uint8_t subbands[2] = { 4, 8 };
void print_sbc_header(struct sbc_frame_hdr *hdr)
static void print_sbc_header(struct sbc_frame_hdr *hdr)
{
RTKBT_DBG("syncword: %02x", hdr->syncword);
RTKBT_DBG("freq %skHz", sample_freqs[hdr->sampling_frequency]);
@@ -979,19 +1006,19 @@ static void packets_count(uint16_t handle, uint16_t scid, uint16_t length,
/* avdtp media data */
if (prof_info->profile_index == profile_a2dp &&
prof_info->flags == A2DP_MEDIA) {
if (!is_profile_busy(profile_a2dp)) {
if (!is_profile_busy(hci_conn, profile_a2dp)) {
struct sbc_frame_hdr *sbc_header;
struct rtp_header *rtph;
u8 bitpool;
update_profile_state(profile_a2dp, TRUE);
update_profile_state(hci_conn, profile_a2dp, TRUE);
if (!direction) {
if (!(hci_conn->profile_bitmap & BIT(profile_sink))) {
btrtl_coex.profile_bitmap |= BIT(profile_sink);
hci_conn->profile_bitmap |= BIT(profile_sink);
update_profile_connection(hci_conn, profile_sink, 1);
}
update_profile_state(profile_sink, TRUE);
update_profile_state(hci_conn, profile_sink, TRUE);
}
/* We assume it is SBC if the packet length
@@ -1019,90 +1046,82 @@ static void packets_count(uint16_t handle, uint16_t scid, uint16_t length,
1, &bitpool);
}
}
btrtl_coex.a2dp_packet_count++;
hci_conn->a2dp_packet_count++;
}
if (prof_info->profile_index == profile_pan)
btrtl_coex.pan_packet_count++;
hci_conn->pan_packet_count++;
}
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0)
static void count_a2dp_packet_timeout(struct timer_list *unused)
#else
static void count_a2dp_packet_timeout(unsigned long data)
#endif
static void count_a2dp_packet_timeout(struct work_struct *work)
{
if (btrtl_coex.a2dp_packet_count)
rtk_conn_prof *hci_conn = container_of(work, rtk_conn_prof,
a2dp_count_work.work);
if (hci_conn->a2dp_packet_count)
RTKBT_DBG("%s: a2dp_packet_count %d", __func__,
btrtl_coex.a2dp_packet_count);
if (btrtl_coex.a2dp_packet_count == 0) {
if (is_profile_busy(profile_a2dp)) {
hci_conn->a2dp_packet_count);
if (hci_conn->a2dp_packet_count == 0) {
if (is_profile_busy(hci_conn, profile_a2dp)) {
RTKBT_DBG("%s: a2dp busy->idle!", __func__);
update_profile_state(profile_a2dp, FALSE);
update_profile_state(hci_conn, profile_a2dp, FALSE);
if (btrtl_coex.profile_bitmap & BIT(profile_sink))
update_profile_state(profile_sink, FALSE);
update_profile_state(hci_conn, profile_sink, FALSE);
}
}
btrtl_coex.a2dp_packet_count = 0;
mod_timer(&btrtl_coex.a2dp_count_timer,
jiffies + msecs_to_jiffies(1000));
hci_conn->a2dp_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->a2dp_count_work, msecs_to_jiffies(1000));
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0)
static void count_pan_packet_timeout(struct timer_list *unused)
#else
static void count_pan_packet_timeout(unsigned long data)
#endif
static void count_pan_packet_timeout(struct work_struct *work)
{
if (btrtl_coex.pan_packet_count)
rtk_conn_prof *hci_conn = container_of(work, rtk_conn_prof,
pan_count_work.work);
if (hci_conn->pan_packet_count)
RTKBT_DBG("%s: pan_packet_count %d", __func__,
btrtl_coex.pan_packet_count);
if (btrtl_coex.pan_packet_count < PAN_PACKET_COUNT) {
if (is_profile_busy(profile_pan)) {
hci_conn->pan_packet_count);
if (hci_conn->pan_packet_count < PAN_PACKET_COUNT) {
if (is_profile_busy(hci_conn, profile_pan)) {
RTKBT_DBG("%s: pan busy->idle!", __func__);
update_profile_state(profile_pan, FALSE);
update_profile_state(hci_conn, profile_pan, FALSE);
}
} else {
if (!is_profile_busy(profile_pan)) {
if (!is_profile_busy(hci_conn, profile_pan)) {
RTKBT_DBG("timeout_handler: pan idle->busy!");
update_profile_state(profile_pan, TRUE);
update_profile_state(hci_conn, profile_pan, TRUE);
}
}
btrtl_coex.pan_packet_count = 0;
mod_timer(&btrtl_coex.pan_count_timer,
jiffies + msecs_to_jiffies(1000));
hci_conn->pan_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->pan_count_work, msecs_to_jiffies(1000));
}
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0)
static void count_hogp_packet_timeout(struct timer_list *unused)
#else
static void count_hogp_packet_timeout(unsigned long data)
#endif
static void count_hogp_packet_timeout(struct work_struct *work)
{
if (btrtl_coex.hogp_packet_count)
rtk_conn_prof *hci_conn = container_of(work, rtk_conn_prof,
hogp_count_work.work);
if (hci_conn->hogp_packet_count)
RTKBT_DBG("%s: hogp_packet_count %d", __func__,
btrtl_coex.hogp_packet_count);
if (btrtl_coex.hogp_packet_count == 0) {
if (is_profile_busy(profile_hogp)) {
hci_conn->hogp_packet_count);
if (hci_conn->hogp_packet_count == 0) {
if (is_profile_busy(hci_conn, profile_hogp)) {
RTKBT_DBG("%s: hogp busy->idle!", __func__);
update_profile_state(profile_hogp, FALSE);
update_profile_state(hci_conn, profile_hogp, FALSE);
}
}
btrtl_coex.hogp_packet_count = 0;
hci_conn->hogp_packet_count = 0;
if (btrtl_coex.voice_packet_count)
if (hci_conn->voice_packet_count)
RTKBT_DBG("%s: voice_packet_count %d", __func__,
btrtl_coex.voice_packet_count);
if (btrtl_coex.voice_packet_count == 0) {
if (is_profile_busy(profile_voice)) {
hci_conn->voice_packet_count);
if (hci_conn->voice_packet_count == 0) {
if (is_profile_busy(hci_conn, profile_voice)) {
RTKBT_DBG("%s: voice busy->idle!", __func__);
update_profile_state(profile_voice, FALSE);
update_profile_state(hci_conn, profile_voice, FALSE);
}
}
btrtl_coex.voice_packet_count = 0;
mod_timer(&btrtl_coex.hogp_count_timer,
jiffies + msecs_to_jiffies(1000));
hci_conn->voice_packet_count = 0;
queue_delayed_work(btrtl_coex.timer_wq, &hci_conn->hogp_count_work, msecs_to_jiffies(1000));
}
#ifdef RTB_SOFTWARE_MAILBOX
@@ -1768,6 +1787,15 @@ static void rtk_handle_cmd_complete_evt(u8 total_len, u8 * p)
rtk_parse_vendor_mailbox_cmd_evt(p, total_len);
}
#endif
if (opcode == HCI_VENDOR_SET_PROFILE_REPORT_COMMAND) {
//0x01-unknown hci command
if((*p++) == 0x01) {
RTKBT_DBG("unknown hci command");
return;
} else {
profileinfo_cmd = 1;
}
}
}
static void rtk_handle_cmd_status_evt(u8 * p)
@@ -1989,7 +2017,7 @@ static u8 disconn_profile(struct rtl_hci_conn *conn, u8 pfe_index)
/* if profile does not exist, status is meaningless */
btrtl_coex.profile_status &= ~(BIT(pfe_index));
rtk_check_del_timer(pfe_index);
rtk_check_del_timer(pfe_index, conn);
}
if (conn->profile_refcount[pfe_index])
@@ -2179,11 +2207,11 @@ static void rtk_parse_event_data(struct rtl_coex_struct *coex,
}
}
const char l2_dir_str[][4] = {
static const char l2_dir_str[][4] = {
"RX", "TX",
};
void rtl_process_l2_sig(struct rtl_l2_buff *l2)
static void rtl_process_l2_sig(struct rtl_l2_buff *l2)
{
/* u8 flag; */
u8 code;
@@ -2371,6 +2399,7 @@ static inline int cmd_cmplt_filter_out(u8 *buf)
#ifdef RTB_SOFTWARE_MAILBOX
case HCI_VENDOR_MAILBOX_CMD:
#endif
case HCI_VENDOR_SET_PROFILE_REPORT_COMMAND:
return 0;
default:
return 1;
@@ -2391,7 +2420,7 @@ static inline int cmd_status_filter_out(u8 *buf)
}
}
int ev_filter_out(u8 *buf)
static int ev_filter_out(u8 *buf)
{
switch (buf[0]) {
case HCI_EV_INQUIRY_COMPLETE:
@@ -2890,6 +2919,14 @@ static inline void rtl_free_frags(struct rtl_coex_struct *coex)
spin_unlock_irqrestore(&coex->rxlock, flags);
}
static void check_profileinfo_cmd(void)
{
//1 + 6 * handle_bumfer, handle_number = 0
uint8_t profileinfo_buf[] = {0x00};
rtk_vendor_cmd_to_fw(HCI_VENDOR_SET_PROFILE_REPORT_COMMAND, 1,
profileinfo_buf);
}
void rtk_btcoex_open(struct hci_dev *hdev)
{
if (test_and_set_bit(RTL_COEX_RUNNING, &btrtl_coex.flags)) {
@@ -2915,22 +2952,6 @@ void rtk_btcoex_open(struct hci_dev *hdev)
#endif /* RTB_SOFTWARE_MAILBOX */
INIT_DELAYED_WORK(&btrtl_coex.l2_work, (void *)rtl_l2_work);
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0)
#ifdef RTB_SOFTWARE_MAILBOX
timer_setup(&btrtl_coex.polling_timer, polling_bt_info, 0);
#endif
timer_setup(&btrtl_coex.a2dp_count_timer, count_a2dp_packet_timeout, 0);
timer_setup(&btrtl_coex.pan_count_timer, count_pan_packet_timeout, 0);
timer_setup(&btrtl_coex.hogp_count_timer, count_hogp_packet_timeout, 0);
#else
#ifdef RTB_SOFTWARE_MAILBOX
setup_timer(&btrtl_coex.polling_timer, polling_bt_info, 0);
#endif
setup_timer(&btrtl_coex.a2dp_count_timer, count_a2dp_packet_timeout, 0);
setup_timer(&btrtl_coex.pan_count_timer, count_pan_packet_timeout, 0);
setup_timer(&btrtl_coex.hogp_count_timer, count_hogp_packet_timeout, 0);
#endif
btrtl_coex.hdev = hdev;
#ifdef RTB_SOFTWARE_MAILBOX
btrtl_coex.wifi_on = 0;
@@ -2950,7 +2971,7 @@ void rtk_btcoex_open(struct hci_dev *hdev)
#endif
rtkbt_coexmsg_send(invite_req, sizeof(invite_req));
#endif
check_profileinfo_cmd();
/* Just for test */
//ctl.polling_enable = 1;
//ctl.polling_time = 1;
@@ -2993,10 +3014,6 @@ void rtk_btcoex_close(void)
}
#endif /* RTB_SOFTWARE_MAILBOX */
del_timer_sync(&btrtl_coex.a2dp_count_timer);
del_timer_sync(&btrtl_coex.pan_count_timer);
del_timer_sync(&btrtl_coex.hogp_count_timer);
cancel_delayed_work_sync(&btrtl_coex.fw_work);
cancel_delayed_work_sync(&btrtl_coex.l2_work);
@@ -3008,6 +3025,7 @@ void rtk_btcoex_close(void)
btrtl_coex.profile_refcount[kk] = 0;
rtl_free_frags(&btrtl_coex);
profileinfo_cmd = 0;
RTKBT_DBG("-x");
}
@@ -3033,6 +3051,7 @@ void rtk_btcoex_init(void)
#endif
#endif /* RTB_SOFTWARE_MAILBOX */
btrtl_coex.fw_wq = create_workqueue("btfwwork");
btrtl_coex.timer_wq = create_workqueue("bttimerwork");
rtl_alloc_buff(&btrtl_coex);
spin_lock_init(&btrtl_coex.rxlock);
}
@@ -3051,5 +3070,7 @@ void rtk_btcoex_exit(void)
#endif
flush_workqueue(btrtl_coex.fw_wq);
destroy_workqueue(btrtl_coex.fw_wq);
flush_workqueue(btrtl_coex.timer_wq);
destroy_workqueue(btrtl_coex.timer_wq);
rtl_free_buff(&btrtl_coex);
}

View File

@@ -40,7 +40,8 @@
//vendor cmd to fw
#define HCI_VENDOR_ENABLE_PROFILE_REPORT_COMMAND 0xfc18
#define HCI_VENDOR_SET_PROFILE_REPORT_COMMAND 0xfc19
#define HCI_VENDOR_SET_PROFILE_REPORT_LEGACY_COMMAND 0xfc19
#define HCI_VENDOR_SET_PROFILE_REPORT_COMMAND 0xfc1B
#define HCI_VENDOR_MAILBOX_CMD 0xfc8f
#define HCI_VENDOR_SET_BITPOOL 0xfc51
@@ -127,7 +128,7 @@
#define PSM_AVDTP 0x0019
#define PSM_FTP 0x1001
#define PSM_BIP 0x1003
#define PSM_OPP 0x1015
#define PSM_OPP 0x1005
//--add more if needed--//
enum {
@@ -159,8 +160,16 @@ typedef struct {
typedef struct rtl_hci_conn {
struct list_head list;
uint16_t handle;
struct delayed_work a2dp_count_work;
struct delayed_work pan_count_work;
struct delayed_work hogp_count_work;
uint32_t a2dp_packet_count;
uint32_t pan_packet_count;
uint32_t hogp_packet_count;
uint32_t voice_packet_count;
uint8_t type; // 0:l2cap, 1:sco/esco, 2:le
uint8_t profile_bitmap;
uint16_t profile_bitmap;
uint16_t profile_status;
int8_t profile_refcount[8];
} rtk_conn_prof, *prtk_conn_prof;
@@ -222,14 +231,12 @@ struct rtl_coex_struct {
struct sockaddr_in wifi_addr;
struct timer_list polling_timer;
#endif
struct timer_list a2dp_count_timer;
struct timer_list pan_count_timer;
struct timer_list hogp_count_timer;
#ifdef RTB_SOFTWARE_MAILBOX
struct workqueue_struct *sock_wq;
struct delayed_work sock_work;
#endif
struct workqueue_struct *fw_wq;
struct workqueue_struct *timer_wq;
struct delayed_work fw_work;
struct delayed_work l2_work;
#ifdef RTB_SOFTWARE_MAILBOX
@@ -238,12 +245,8 @@ struct rtl_coex_struct {
struct urb *urb;
spinlock_t spin_lock_sock;
spinlock_t spin_lock_profile;
uint32_t a2dp_packet_count;
uint32_t pan_packet_count;
uint32_t hogp_packet_count;
uint32_t voice_packet_count;
uint8_t profile_bitmap;
uint8_t profile_status;
uint16_t profile_bitmap;
uint16_t profile_status;
int8_t profile_refcount[8];
uint8_t ispairing;
uint8_t isinquirying;

View File

@@ -83,6 +83,7 @@ static struct list_head list_extracfgs;
#define HCI_VENDOR_CHANGE_BDRATE 0xfc17
#define HCI_VENDOR_READ_RTK_ROM_VERISION 0xfc6d
#define HCI_VENDOR_READ_LMP_VERISION 0x1001
#define HCI_VENDOR_READ_CMD 0xfc61
#define ROM_LMP_NONE 0x0000
#define ROM_LMP_8723a 0x1200
@@ -92,11 +93,34 @@ static struct list_head list_extracfgs;
#define ROM_LMP_8822b 0X8822
#define ROM_LMP_8852a 0x8852
#define PATCH_SNIPPETS 0x01
#define PATCH_DUMMY_HEADER 0x02
#define PATCH_SECURITY_HEADER 0x03
#define PATCH_OTA_FLAG 0x04
#define SECTION_HEADER_SIZE 8
struct rtk_eversion_evt {
uint8_t status;
uint8_t version;
} __attribute__ ((packed));
struct rtk_security_proj_evt {
uint8_t status;
uint8_t key_id;
} __attribute__ ((packed));
struct rtk_chip_type_evt {
uint8_t status;
uint16_t chip;
} __attribute__ ((packed));
enum rtk_read_class {
READ_NONE = 0,
READ_CHIP_TYPE = 1,
READ_CHIP_VER = 2,
READ_SEC_PROJ = 3
};
struct rtk_epatch_entry {
uint16_t chipID;
uint16_t patch_length;
@@ -105,8 +129,8 @@ struct rtk_epatch_entry {
struct rtk_epatch {
uint8_t signature[8];
uint32_t fw_version;
uint16_t number_of_total_patch;
__le32 fw_version;
__le16 number_of_total_patch;
struct rtk_epatch_entry entry[0];
} __attribute__ ((packed));
@@ -116,13 +140,30 @@ struct rtk_extension_entry {
uint8_t *data;
} __attribute__ ((packed));
//signature: Realtech
const uint8_t RTK_EPATCH_SIGNATURE[8] =
{ 0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68 };
//Extension Section IGNATURE:0x77FD0451
const uint8_t Extension_Section_SIGNATURE[4] = { 0x51, 0x04, 0xFD, 0x77 };
struct rtb_section_hdr {
uint32_t opcode;
uint32_t section_len;
uint32_t soffset;
} __attribute__ ((packed));
uint16_t project_id[] = {
struct rtb_new_patch_hdr {
uint8_t signature[8];
uint8_t fw_version[8];
__le32 number_of_section;
} __attribute__ ((packed));
//signature: Realtech
static const uint8_t RTK_EPATCH_SIGNATURE[8] =
{ 0x52, 0x65, 0x61, 0x6C, 0x74, 0x65, 0x63, 0x68 };
//signature: RTBTCore
static const uint8_t RTK_EPATCH_SIGNATURE_NEW[8] =
{ 0x52, 0x54, 0x42, 0x54, 0x43, 0x6F, 0x72, 0x65 };
//Extension Section IGNATURE:0x77FD0451
static const uint8_t Extension_Section_SIGNATURE[4] = { 0x51, 0x04, 0xFD, 0x77 };
static uint16_t project_id[] = {
ROM_LMP_8723a,
ROM_LMP_8723b,
ROM_LMP_8821a,
@@ -149,6 +190,14 @@ uint16_t project_id[] = {
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_8852a, /* index 25 for 8852CU */
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_NONE,
ROM_LMP_8822b, /* index 33 for 8822EU */
};
enum rtk_endpoit {
@@ -169,6 +218,7 @@ enum rtk_endpoit {
#define RTL8723FU 0x76
#define RTL8852BU 0x77
#define RTL8852CU 0x78
#define RTL8822EU 0x79
typedef struct {
uint16_t prod_id;
@@ -210,21 +260,22 @@ typedef struct {
} __attribute__ ((packed)) download_rp;
#define RTK_VENDOR_CONFIG_MAGIC 0x8723ab55
const u8 cfg_magic[4] = { 0x55, 0xab, 0x23, 0x87 };
static const u8 cfg_magic[4] = { 0x55, 0xab, 0x23, 0x87 };
struct rtk_bt_vendor_config_entry {
uint16_t offset;
__le16 offset;
uint8_t entry_len;
uint8_t entry_data[0];
} __attribute__ ((packed));
struct rtk_bt_vendor_config {
uint32_t signature;
uint16_t data_len;
__le32 signature;
__le16 data_len;
struct rtk_bt_vendor_config_entry entry[0];
} __attribute__ ((packed));
#define BT_CONFIG_HDRLEN sizeof(struct rtk_bt_vendor_config)
static uint8_t gEVersion = 0xFF;
static uint8_t g_key_id = 0;
static dev_data *dev_data_find(struct usb_interface *intf);
static patch_info *get_patch_entry(struct usb_device *udev);
@@ -235,6 +286,7 @@ static int download_data(xchange_data * xdata);
static int send_hci_cmd(xchange_data * xdata);
static int rcv_hci_evt(xchange_data * xdata);
static uint8_t rtk_get_eversion(dev_data * dev_entry);
static int rtk_vendor_read(dev_data * dev_entry, uint8_t class);
static patch_info fw_patch_table[] = {
/* { pid, lmp_sub, mp_fw_name, fw_name, config_name, chip_type } */
@@ -313,6 +365,9 @@ static patch_info fw_patch_table[] = {
{0x3533, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3538, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3539, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3558, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3559, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3581, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for Azurewave */
{0x3540, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE */
{0x3541, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for GSD */
{0x3543, 0x8821, "mp_rtl8821cu_fw", "rtl8821cu_fw", "rtl8821cu_config", RTL8821CU}, /* RTL8821CE for GSD */
@@ -321,7 +376,7 @@ static patch_info fw_patch_table[] = {
{0xc82c, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */
{0xc82e, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */
{0xc81d, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CU */
{0xd820, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8821DU */
{0xd820, 0x8822, "mp_rtl8821du_fw", "rtl8821du_fw", "rtl8821du_config", RTL8822CU}, /* RTL8821DU */
{0xc822, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */
{0xc82b, 0x8822, "mp_rtl8822cu_fw", "rtl8822cu_fw", "rtl8822cu_config", RTL8822CU}, /* RTL8822CE */
@@ -379,8 +434,8 @@ static patch_info fw_patch_table[] = {
{0xb73a, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */
{0xf72b, 0x8723, "mp_rtl8723fu_fw", "rtl8723fu_fw", "rtl8723fu_config", RTL8723FU}, /* RTL8723FU */
{0x8851, 0x8852, "mp_rtl8851au_fw", "rtl8851au_fw", "rtl8851au_config", RTL8852BU}, /* RTL8851AU */
{0xa85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BU */
{0x8851, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8851AU */
{0xb85b, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */
{0xb85c, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */
{0x3571, 0x8852, "mp_rtl8852bu_fw", "rtl8852bu_fw", "rtl8852bu_config", RTL8852BU}, /* RTL8852BE */
@@ -406,13 +461,16 @@ static patch_info fw_patch_table[] = {
{0x887c, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */
{0x4007, 0x8852, "mp_rtl8852cu_fw", "rtl8852cu_fw", "rtl8852cu_config", RTL8852CU}, /* RTL8852CE */
{0xe822, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */
{0xa82a, 0x8822, "mp_rtl8822eu_fw", "rtl8822eu_fw", "rtl8822eu_config", RTL8822EU}, /* RTL8822EU */
/* NOTE: must append patch entries above the null entry */
{0, 0, NULL, NULL, NULL, 0}
};
static LIST_HEAD(dev_data_list);
void util_hexdump(const u8 *buf, size_t len)
static void util_hexdump(const u8 *buf, size_t len)
{
static const char hexdigits[] = "0123456789abcdef";
char str[16 * 3];
@@ -452,48 +510,6 @@ int __rtk_send_hci_cmd(struct usb_device *udev, u8 *buf, u16 size)
return result;
}
int __rtk_recv_hci_evt(struct usb_device *udev, u8 *buf, u8 len, u16 opcode)
{
int recv_length = 0;
int result = 0;
int i;
unsigned int pipe = usb_rcvintpipe(udev, 1);
struct hci_event_hdr *hdr;
struct hci_ev_cmd_complete *cmd_cmpl;
if (len < sizeof(*hdr) + sizeof(*cmd_cmpl)) {
RTKBT_ERR("%s: Invalid buf length %u", __func__, len);
return -1;
}
while (1) {
for (i = 0; i < 5; i++) {
result = usb_interrupt_msg(udev, pipe,
(void *)buf, PKT_LEN,
&recv_length, MSG_TO);
if (result >= 0)
break;
}
if (result < 0) {
RTKBT_ERR("%s; Couldn't receive HCI event, err %d",
__func__, result);
return result;
}
/* Ignore the event which is not command complete event */
if (recv_length < sizeof(*hdr) + sizeof(*cmd_cmpl))
continue;
hdr = (struct hci_event_hdr *)buf;
cmd_cmpl = (struct hci_ev_cmd_complete *)(buf + sizeof(*hdr));
if (hdr->evt == 0x0e) {
if (opcode == cmd_cmpl->opcode)
return recv_length;
}
}
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
@@ -643,7 +659,7 @@ int patch_add(struct usb_interface *intf)
}
udev = interface_to_usbdev(intf);
#if BTUSB_RPM
#ifdef BTUSB_RPM
RTKBT_DBG("auto suspend is enabled");
usb_enable_autosuspend(udev);
pm_runtime_set_autosuspend_delay(&(udev->dev), 2000);
@@ -679,7 +695,7 @@ void patch_remove(struct usb_interface *intf)
struct usb_device *udev;
udev = interface_to_usbdev(intf);
#if BTUSB_RPM
#ifdef BTUSB_RPM
usb_disable_autosuspend(udev);
#endif
@@ -747,6 +763,9 @@ static inline int get_max_patch_size(u8 chip_type)
case RTL8852CU:
max_patch_size = 0x130D0 + 529; /* 76.2KB */
break;
case RTL8822EU:
max_patch_size = 0x24620 + 529; /* 145KB */
break;
default:
max_patch_size = 40 * 1024;
break;
@@ -755,6 +774,38 @@ static inline int get_max_patch_size(u8 chip_type)
return max_patch_size;
}
static int check_fw_chip_ver(dev_data * dev_entry, xchange_data * xdata)
{
int ret_val;
uint16_t chip = 0;
uint16_t chip_ver = 0;
chip = rtk_vendor_read(dev_entry, READ_CHIP_TYPE);
if(chip == 0x8822) {
chip_ver = rtk_vendor_read(dev_entry, READ_CHIP_VER);
if(chip_ver == 0x000e) {
return 0;
}
}
ret_val = check_fw_version(xdata);
if (ret_val < 0) {
RTKBT_ERR("Failed to get Local Version Information");
return ret_val;
} else if (ret_val > 0) {
RTKBT_DBG("Firmware already exists");
/* Patch alread exists, just return */
if (gEVersion == 0xff) {
RTKBT_DBG("global_version is not set, get it!");
gEVersion = rtk_get_eversion(dev_entry);
}
return ret_val;
}
return 0;
}
int download_patch(struct usb_interface *intf)
{
dev_data *dev_entry;
@@ -781,21 +832,10 @@ int download_patch(struct usb_interface *intf)
init_xdata(xdata, dev_entry);
ret_val = check_fw_version(xdata);
if (ret_val < 0) {
RTKBT_ERR("Failed to get Local Version Information");
ret_val = check_fw_chip_ver(dev_entry, xdata);
if (ret_val != 0 )
goto patch_end;
} else if (ret_val > 0) {
RTKBT_DBG("Firmware already exists");
/* Patch alread exists, just return */
if (gEVersion == 0xff) {
RTKBT_DBG("global_version is not set, get it!");
gEVersion = rtk_get_eversion(dev_entry);
}
goto patch_end;
}
xdata->fw_len = load_firmware(dev_entry, &xdata->fw_data);
if (xdata->fw_len <= 0) {
RTKBT_ERR("load firmware failed!");
@@ -856,6 +896,7 @@ patch_end:
int download_lps_patch(struct usb_interface *intf)
{
dev_data *dev_entry;
patch_info *pinfo;
xchange_data *xdata = NULL;
uint8_t *fw_buf;
int result;
@@ -863,6 +904,7 @@ int download_lps_patch(struct usb_interface *intf)
char *origin_name1;
char name2[64];
char *origin_name2;
int max_patch_size = 0;
RTKBT_DBG("Download LPS Patch start");
dev_entry = dev_data_find(intf);
@@ -956,6 +998,7 @@ patch_end:
}
#endif
#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH
int set_scan(struct usb_interface *intf)
{
dev_data *dev_entry;
@@ -989,7 +1032,6 @@ int set_scan(struct usb_interface *intf)
if (result < 0)
goto end;
result = rcv_hci_evt(xdata);
end:
kfree(xdata->send_pkt);
kfree(xdata->rcv_pkt);
@@ -999,6 +1041,7 @@ end:
return result;
}
#endif
dev_data *dev_data_find(struct usb_interface * intf)
{
@@ -1055,6 +1098,7 @@ static int is_mac(u8 chip_type, u16 offset)
case RTL8723FU:
case RTL8852BU:
case RTL8852CU:
case RTL8822EU:
if (offset == 0x0030)
return 1;
break;
@@ -1080,6 +1124,7 @@ static uint16_t get_mac_offset(u8 chip_type)
case RTL8723FU:
case RTL8852BU:
case RTL8852CU:
case RTL8822EU:
return 0x0030;
case RTLPREVIOUS:
return 0x003c;
@@ -1148,7 +1193,7 @@ static void merge_configs(struct list_head *head, struct list_head *head2)
}
}
int rtk_parse_config_file(u8 *config_buf, int filelen)
static int rtk_parse_config_file(u8 *config_buf, int filelen)
{
struct rtk_bt_vendor_config *config = (void *)config_buf;
u16 config_len = 0, temp = 0;
@@ -1196,10 +1241,10 @@ int rtk_parse_config_file(u8 *config_buf, int filelen)
temp);
}
return 0;;
return 0;
}
uint8_t rtk_get_fw_project_id(uint8_t * p_buf)
static uint8_t rtk_get_fw_project_id(uint8_t * p_buf)
{
uint8_t opcode;
uint8_t len;
@@ -1227,43 +1272,234 @@ uint8_t rtk_get_fw_project_id(uint8_t * p_buf)
return data;
}
static void rtk_get_patch_entry(uint8_t * epatch_buf,
struct rtb_ota_flag {
uint8_t eco;
uint8_t enable;
uint16_t reserve;
} __attribute__ ((packed));
struct rtb_security_hdr {
uint8_t eco;
uint8_t pri;
uint8_t key_id;
uint8_t reserve;
uint32_t security_len;
uint8_t *payload;
} __attribute__ ((packed));
struct rtb_dummy_hdr {
uint8_t eco;
uint8_t pri;
uint8_t reserve;
uint32_t dummy_len;
uint8_t *payload;
} __attribute__ ((packed));
struct rtb_snippet_hdr {
uint8_t eco;
uint8_t pri;
uint16_t reserve;
uint32_t snippet_len;
uint8_t *payload;
} __attribute__ ((packed));
struct patch_node {
uint8_t eco;
uint8_t pri;
uint8_t key_id;
uint8_t reserve;
uint32_t len;
uint8_t *payload;
struct list_head list;
} __attribute__ ((packed));
/* Add a node to alist that is in ascending order. */
static void insert_queue_sort(struct list_head *head, struct patch_node *node)
{
struct list_head *pos;
struct list_head *next;
struct patch_node *tmp;
if(!head || !node) {
return;
}
list_for_each_safe(pos, next, head) {
tmp = list_entry(pos, struct patch_node, list);
if(tmp->pri >= node->pri)
break;
}
__list_add(&node->list, pos->prev, pos);
}
static int insert_patch(struct patch_node *patch_node_hdr, uint8_t *section_pos,
uint32_t opcode, uint32_t *patch_len, uint8_t *sec_flag)
{
struct patch_node *tmp;
int i;
uint32_t numbers;
uint32_t section_len = 0;
uint8_t eco = 0;
uint8_t *pos = section_pos + 8;
numbers = get_unaligned_le16(pos);
RTKBT_DBG("number 0x%04x", numbers);
pos += 4;
for (i = 0; i < numbers; i++) {
eco = (uint8_t)*(pos);
RTKBT_DBG("eco 0x%02x, Eversion:%02x", eco, gEVersion);
if (eco == gEVersion + 1) {
tmp = (struct patch_node*)kzalloc(sizeof(struct patch_node), GFP_KERNEL);
tmp->pri = (uint8_t)*(pos + 1);
if(opcode == PATCH_SECURITY_HEADER)
tmp->key_id = (uint8_t)*(pos + 1);
section_len = get_unaligned_le32(pos + 4);
tmp->len = section_len;
*patch_len += section_len;
RTKBT_DBG("Pri:%d, Patch length 0x%04x", tmp->pri, tmp->len);
tmp->payload = pos + 8;
if(opcode != PATCH_SECURITY_HEADER) {
insert_queue_sort(&(patch_node_hdr->list), tmp);
} else {
if((g_key_id == tmp->key_id) && (g_key_id > 0)) {
insert_queue_sort(&(patch_node_hdr->list), tmp);
*sec_flag = 1;
} else {
pos += (8 + section_len);
kfree(tmp);
continue;
}
}
} else {
section_len = get_unaligned_le32(pos + 4);
RTKBT_DBG("Patch length 0x%04x", section_len);
}
pos += (8 + section_len);
}
return 0;
}
static uint8_t *rtb_get_patch_header(int *len,
struct patch_node *patch_node_hdr, uint8_t * epatch_buf,
uint8_t key_id)
{
uint16_t i, j;
struct rtb_new_patch_hdr *new_patch;
uint8_t sec_flag = 0;
uint32_t number_of_ota_flag;
uint32_t patch_len = 0;
uint8_t *section_pos;
uint8_t *ota_flag_pos;
uint32_t number_of_section;
struct rtb_section_hdr section_hdr;
struct rtb_ota_flag ota_flag;
new_patch = (struct rtb_new_patch_hdr *)epatch_buf;
number_of_section = le32_to_cpu(new_patch->number_of_section);
RTKBT_DBG("FW version 0x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x",
*(epatch_buf + 8), *(epatch_buf + 9), *(epatch_buf + 10),
*(epatch_buf + 11),*(epatch_buf + 12), *(epatch_buf + 13),
*(epatch_buf + 14), *(epatch_buf + 15));
section_pos = epatch_buf + 20;
for (i = 0; i < number_of_section; i++) {
section_hdr.opcode = get_unaligned_le32(section_pos);
section_hdr.section_len = get_unaligned_le32(section_pos + 4);
RTKBT_DBG("opcode 0x%04x", section_hdr.opcode);
switch (section_hdr.opcode) {
case PATCH_SNIPPETS:
insert_patch(patch_node_hdr, section_pos, PATCH_SNIPPETS, &patch_len, NULL);
break;
case PATCH_SECURITY_HEADER:
if(!g_key_id)
break;
sec_flag = 0;
insert_patch(patch_node_hdr, section_pos, PATCH_SECURITY_HEADER, &patch_len, &sec_flag);
if(sec_flag)
break;
for (i = 0; i < number_of_section; i++) {
section_hdr.opcode = get_unaligned_le32(section_pos);
section_hdr.section_len = get_unaligned_le32(section_pos + 4);
if(section_hdr.opcode == PATCH_DUMMY_HEADER) {
insert_patch(patch_node_hdr, section_pos, PATCH_DUMMY_HEADER, &patch_len, NULL);
}
section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len);
}
break;
case PATCH_DUMMY_HEADER:
if(g_key_id) {
break;
}
insert_patch(patch_node_hdr, section_pos, PATCH_DUMMY_HEADER, &patch_len, NULL);
break;
case PATCH_OTA_FLAG:
ota_flag_pos = section_pos + 4;
number_of_ota_flag = get_unaligned_le32(ota_flag_pos);
ota_flag.eco = (uint8_t)*(ota_flag_pos + 1);
if (ota_flag.eco == gEVersion + 1) {
for (j = 0; j < number_of_ota_flag; j++) {
if (ota_flag.eco == gEVersion + 1) {
ota_flag.enable = get_unaligned_le32(ota_flag_pos + 4);
}
}
}
break;
default:
RTKBT_ERR("Wrong Opcode");
goto wrong_opcode;
}
section_pos += (SECTION_HEADER_SIZE + section_hdr.section_len);
}
*len = patch_len;
wrong_opcode:
return NULL;
}
static int rtk_get_patch_entry(uint8_t * epatch_buf,
struct rtk_epatch_entry *entry)
{
uint32_t svn_ver;
uint32_t coex_ver;
uint32_t tmp;
uint16_t i;
uint16_t number_of_total_patch;
struct rtk_epatch *epatch_info = (struct rtk_epatch *)epatch_buf;
epatch_info->number_of_total_patch =
number_of_total_patch =
le16_to_cpu(epatch_info->number_of_total_patch);
RTKBT_DBG("fw_version = 0x%x", le32_to_cpu(epatch_info->fw_version));
RTKBT_DBG("number_of_total_patch = %d",
epatch_info->number_of_total_patch);
RTKBT_DBG("number_of_total_patch = %d", number_of_total_patch);
/* get right epatch entry */
for (i = 0; i < epatch_info->number_of_total_patch; i++) {
for (i = 0; i < number_of_total_patch; i++) {
if (get_unaligned_le16(epatch_buf + 14 + 2 * i) ==
gEVersion + 1) {
entry->chipID = gEVersion + 1;
entry->patch_length = get_unaligned_le16(epatch_buf +
14 +
2 * epatch_info->number_of_total_patch +
2 * number_of_total_patch +
2 * i);
entry->start_offset = get_unaligned_le32(epatch_buf +
14 +
4 * epatch_info-> number_of_total_patch +
4 * number_of_total_patch +
4 * i);
break;
}
}
if (i >= epatch_info->number_of_total_patch) {
if (i >= number_of_total_patch) {
entry->patch_length = 0;
entry->start_offset = 0;
RTKBT_ERR("No corresponding patch found\n");
return;
return 0;
}
svn_ver = get_unaligned_le32(epatch_buf +
@@ -1281,9 +1517,11 @@ static void rtk_get_patch_entry(uint8_t * epatch_buf,
tmp = ((coex_ver >> 16) & 0x7ff) + (coex_ver >> 27) * 10000;
RTKBT_DBG("Coexistence: BTCOEX_20%06d-%04x",
tmp, (coex_ver & 0xffff));
return 0;
}
int bachk(const char *str)
static int bachk(const char *str)
{
if (!str)
return -1;
@@ -1392,7 +1630,7 @@ static u8 *load_config(dev_data *dev_entry, int *length)
RTKBT_INFO("config filename %s", config_name);
result = request_firmware(&fw, config_name, &udev->dev);
if (result < 0)
return 0;
return NULL;
file_sz = fw->size;
buf = (u8 *)fw->data;
@@ -1496,6 +1734,95 @@ done:
return buf;
}
static int rtk_vendor_read(dev_data * dev_entry, uint8_t class)
{
struct rtk_chip_type_evt *chip_type;
struct rtk_security_proj_evt *sec_proj;
patch_info *patch_entry;
int ret_val = 0;
xchange_data *xdata = NULL;
unsigned char cmd_ct_buf[] = {0x10, 0x38, 0x04, 0x28, 0x80};
unsigned char cmd_cv_buf[] = {0x10, 0x3A, 0x04, 0x28, 0x80};
unsigned char cmd_sec_buf[] = {0x10, 0xA4, 0x0D, 0x00, 0xb0};
xdata = kzalloc(sizeof(xchange_data), GFP_KERNEL);
if (NULL == xdata) {
ret_val = 0xFE;
RTKBT_DBG("NULL == xdata");
return ret_val;
}
init_xdata(xdata, dev_entry);
xdata->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_CMD);
xdata->cmd_hdr->plen = 5;
memcpy(xdata->send_pkt, &(xdata->cmd_hdr->opcode), 2);
memcpy(xdata->send_pkt+2, &(xdata->cmd_hdr->plen), 1);
switch (class) {
case READ_CHIP_TYPE:
memcpy(xdata->send_pkt+3, cmd_ct_buf, sizeof(cmd_ct_buf));
break;
case READ_CHIP_VER:
memcpy(xdata->send_pkt+3, cmd_cv_buf, sizeof(cmd_cv_buf));
break;
case READ_SEC_PROJ:
memcpy(xdata->send_pkt+3, cmd_sec_buf, sizeof(cmd_sec_buf));
break;
default:
break;
}
xdata->pkt_len = CMD_HDR_LEN + 5;
ret_val = send_hci_cmd(xdata);
if (ret_val < 0) {
RTKBT_ERR("Failed to send read RTK chip_type cmd.");
ret_val = 0xFE;
goto read_end;
}
ret_val = rcv_hci_evt(xdata);
if (ret_val < 0) {
RTKBT_ERR("Failed to receive HCI event for chip type.");
ret_val = 0xFE;
goto read_end;
}
patch_entry = xdata->dev_entry->patch_entry;
if(class == READ_SEC_PROJ){
sec_proj = (struct rtk_security_proj_evt *)(xdata->rsp_para);
RTKBT_DBG("sec_proj->status = 0x%x, sec_proj->key_id = 0x%x",
sec_proj->status, sec_proj->key_id);
if (sec_proj->status) {
ret_val = 0;
} else {
ret_val = sec_proj->key_id;
g_key_id = sec_proj->key_id;
}
} else {
chip_type = (struct rtk_chip_type_evt *)(xdata->rsp_para);
RTKBT_DBG("chip_type->status = 0x%x, chip_type->chip = 0x%x",
chip_type->status, chip_type->chip);
if (chip_type->status) {
ret_val = 0;
} else {
ret_val = chip_type->chip;
}
}
read_end:
if (xdata != NULL) {
if (xdata->send_pkt)
kfree(xdata->send_pkt);
if (xdata->rcv_pkt)
kfree(xdata->rcv_pkt);
kfree(xdata);
}
return ret_val;
}
int load_firmware(dev_data * dev_entry, uint8_t ** buff)
{
const struct firmware *fw;
@@ -1509,6 +1836,10 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
uint16_t lmp_version;
struct rtk_epatch_entry current_entry = { 0 };
struct list_head *pos, *next;
struct patch_node *tmp;
struct patch_node patch_node_hdr;
RTKBT_DBG("load_firmware start");
udev = dev_entry->udev;
patch_entry = dev_entry->patch_entry;
@@ -1518,15 +1849,18 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
config_file_buf = load_config(dev_entry, &config_len);
fw_name = patch_entry->patch_name;
RTKBT_ERR("fw name is %s", fw_name);
RTKBT_DBG("fw name is %s", fw_name);
ret_val = request_firmware(&fw, fw_name, &udev->dev);
if (ret_val < 0) {
RTKBT_ERR("request_firmware error");
fw_len = 0;
kfree(config_file_buf);
config_file_buf = NULL;
goto fw_fail;
}
INIT_LIST_HEAD(&patch_node_hdr.list);
epatch_buf = kzalloc(fw->size, GFP_KERNEL);
if (NULL == epatch_buf)
goto alloc_fail;
@@ -1567,7 +1901,7 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
}
/* check Signature and Extension Section Field */
if ((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) != 0) ||
if (((memcmp(epatch_buf, RTK_EPATCH_SIGNATURE, 8) != 0) && (memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) != 0))||
memcmp(epatch_buf + buf_len - config_len - 4,
Extension_Section_SIGNATURE, 4) != 0) {
RTKBT_ERR("Check SIGNATURE error! do not download fw");
@@ -1586,30 +1920,65 @@ int load_firmware(dev_data * dev_entry, uint8_t ** buff)
RTKBT_DBG
("lmp_version is %x, project_id is %x, match!",
lmp_version, project_id[proj_id]);
rtk_get_patch_entry(epatch_buf, &current_entry);
if (current_entry.patch_length == 0)
goto alloc_fail;
if(memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) {
int key_id = rtk_vendor_read(dev_entry, READ_SEC_PROJ);
RTKBT_DBG("%s: key id %d", __func__, key_id);
if (key_id < 0) {
RTKBT_ERR("%s: Read key id failure", __func__);
need_download_fw = 0;
fw_len = 0;
goto alloc_fail;
}
rtb_get_patch_header(&buf_len, &patch_node_hdr, epatch_buf, key_id);
if(buf_len == 0)
goto alloc_fail;
RTKBT_DBG("buf_len = 0x%x", buf_len);
buf_len += config_len;
} else {
rtk_get_patch_entry(epatch_buf, &current_entry);
buf_len =
current_entry.patch_length + config_len;
RTKBT_DBG("buf_len = 0x%x", buf_len);
if (current_entry.patch_length == 0)
goto alloc_fail;
buf_len = current_entry.patch_length + config_len;
RTKBT_DBG("buf_len = 0x%x", buf_len);
}
if (!(buf = kzalloc(buf_len, GFP_KERNEL))) {
RTKBT_ERR
("Can't alloc memory for multi fw&config");
buf_len = -1;
} else {
memcpy(buf,
epatch_buf +
current_entry.start_offset,
current_entry.patch_length);
memcpy(buf + current_entry.patch_length - 4, epatch_buf + 8, 4); /*fw version */
if (config_len) {
memcpy(&buf
[buf_len - config_len],
config_file_buf,
config_len);
if(memcmp(epatch_buf, RTK_EPATCH_SIGNATURE_NEW, 8) == 0) {
int tmp_len = 0;
list_for_each_safe(pos, next, &patch_node_hdr.list)
{
tmp = list_entry(pos, struct patch_node, list);
RTKBT_DBG("len = 0x%x", tmp->len);
memcpy(buf + tmp_len, tmp->payload, tmp->len);
tmp_len += tmp->len;
list_del_init(pos);
kfree(tmp);
}
if (config_len) {
memcpy(&buf
[buf_len - config_len],
config_file_buf,
config_len);
}
} else {
memcpy(buf,
epatch_buf +
current_entry.start_offset,
current_entry.patch_length);
memcpy(buf + current_entry.patch_length - 4, epatch_buf + 8, 4); /*fw version */
if (config_len) {
memcpy(&buf
[buf_len - config_len],
config_file_buf,
config_len);
}
}
}
}
@@ -1662,6 +2031,7 @@ int check_fw_version(xchange_data * xdata)
patch_info *patch_entry;
int ret_val;
int retry = 0;
uint16_t lmp_subver, hci_rev, manufacturer;
/* Ensure that the first cmd is hci reset after system suspend
* or system reboot */
@@ -1686,14 +2056,14 @@ get_ver:
patch_entry = xdata->dev_entry->patch_entry;
read_ver_rsp = (struct hci_rp_read_local_version *)(xdata->rsp_para);
read_ver_rsp->lmp_subver = le16_to_cpu(read_ver_rsp->lmp_subver);
read_ver_rsp->hci_rev = le16_to_cpu(read_ver_rsp->hci_rev);
read_ver_rsp->manufacturer = le16_to_cpu(read_ver_rsp->manufacturer);
lmp_subver = le16_to_cpu(read_ver_rsp->lmp_subver);
hci_rev = le16_to_cpu(read_ver_rsp->hci_rev);
manufacturer = le16_to_cpu(read_ver_rsp->manufacturer);
RTKBT_DBG("read_ver_rsp->lmp_subver = 0x%x", read_ver_rsp->lmp_subver);
RTKBT_DBG("read_ver_rsp->hci_rev = 0x%x", read_ver_rsp->hci_rev);
RTKBT_DBG("read_ver_rsp->lmp_subver = 0x%x", lmp_subver);
RTKBT_DBG("read_ver_rsp->hci_rev = 0x%x", hci_rev);
RTKBT_DBG("patch_entry->lmp_sub = 0x%x", patch_entry->lmp_sub);
if (patch_entry->lmp_sub != read_ver_rsp->lmp_subver) {
if (patch_entry->lmp_sub != lmp_subver) {
return 1;
}

View File

@@ -20,6 +20,12 @@
* Download normal patch when host resume or power on */
/* #define RTKBT_SWITCH_PATCH */
/* RTKBT Power-on for sideband wake-up by LE Advertising from Remote. */
/* Note that it's necessary to apply TV FW Patch. */
/* #define RTKBT_SUSPEND_WAKEUP */
/* #define RTKBT_SHUTDOWN_WAKEUP */
#define RTKBT_POWERKEY_WAKEUP
/* RTKBT Power-on Whitelist for sideband wake-up by LE Advertising from Remote.
* Note that it's necessary to apply TV FW Patch. */
/* #define RTKBT_TV_POWERON_WHITELIST */
@@ -39,9 +45,7 @@
#endif
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 33)
#define USB_RPM 1
#else
#define USB_RPM 0
#define USB_RPM
#endif
#define CONFIG_NEEDS_BINDING
@@ -51,9 +55,10 @@
#undef CONFIG_NEEDS_BINDING
#endif
/* 1 SS enable; 0 SS disable */
#define BTUSB_RPM (0*USB_RPM)
/* USB SS */
#if (defined CONFIG_BTUSB_AUTOSUSPEND) && (defined USB_RPM)
#define BTUSB_RPM
#endif
#define PRINT_CMD_EVENT 0
#define PRINT_ACL_DATA 0
@@ -67,7 +72,6 @@ extern void print_acl(struct sk_buff *skb, int dataOut);
#if defined RTKBT_SWITCH_PATCH || defined RTKBT_TV_POWERON_WHITELIST
int __rtk_send_hci_cmd(struct usb_device *udev, u8 *buf, u16 size);
int __rtk_recv_hci_evt(struct usb_device *udev, u8 *buf, u8 len, u16 opcode);
#endif
#ifdef RTKBT_SWITCH_PATCH
@@ -79,8 +83,10 @@ struct api_context {
};
int download_lps_patch(struct usb_interface *intf);
int set_scan(struct usb_interface *intf);
#endif
#if defined RTKBT_SUSPEND_WAKEUP || defined RTKBT_SHUTDOWN_WAKEUP || defined RTKBT_SWITCH_PATCH
int set_scan(struct usb_interface *intf);
#endif