337 lines
9.7 KiB
C
Raw Normal View History

2025-05-10 21:58:58 +08:00
/*
* Copyright (C) 2018 Spreadtrum Communications Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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/atomic.h>
#include <linux/compat.h>
#include <linux/console.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/vt_kern.h>
#include <linux/workqueue.h>
#include "include/debug.h"
#include "include/hci.h"
#include "include/sdio.h"
static struct semaphore sem_id;
static struct bt_data_interface_cb_t* bt_data_if_cb;
static int need_indicate_cp2_woble = 0;
int resume_transfer_upper(const unsigned char* buf, int count);
int set_need_indicate_cp2_woble(int set)
{
pr_err("%s %d\n", __func__, set);
need_indicate_cp2_woble = set;
return 0;
}
static int marlin_sdio_tx_cb(int chn, struct mbuf_t* head, struct mbuf_t* tail, int num)
{
int i;
struct mbuf_t* pos = NULL;
BT_VER("%s channel: %d, head: %p, tail: %p num: %d\n", __func__, chn, head, tail, num);
pos = head;
for (i = 0; i < num; i++, pos = pos->next) {
kfree(pos->buf);
pos->buf = NULL;
}
if ((sprdwcn_bus_list_free(chn, head, tail, num)) == 0) {
BT_VER("%s sprdwcn_bus_list_free() success\n", __func__);
up(&sem_id);
} else {
pr_err("%s sprdwcn_bus_list_free() fail\n", __func__);
}
return 0;
}
static int marlin_sdio_rx_cb(int chn, struct mbuf_t* head, struct mbuf_t* tail, int num)
{
int block_size;
block_size = ((head->buf[2] & 0x7F) << 9) + (head->buf[1] << 1) + (head->buf[0] >> 7);
BT_VER("%s +++\n", __func__);
if (bt_data_if_cb == NULL) {
return -1;
}
BT_VERDUMP((unsigned char*)head->buf + BT_SDIO_HEAD_LEN, block_size);
bt_data_if_cb->recv((unsigned char*)head->buf + BT_SDIO_HEAD_LEN, block_size);
sprdwcn_bus_push_list(chn, head, tail, num);
BT_VER("%s ---\n", __func__);
return 0;
}
int marlin_sdio_write(const unsigned char* buf, int count)
{
int num = 1, ret;
struct mbuf_t *tx_head = NULL, *tx_tail = NULL;
unsigned char* block = NULL;
const unsigned char* tmp_buf = buf + 1;
unsigned short op = 0;
int i = 0;
STREAM_TO_UINT16(op, tmp_buf);
if (buf[0] == 0x1) {
BT_DEBUG("%s +++ op 0x%04X\n", __func__, op);
}
block = kmalloc(count + BT_SDIO_HEAD_LEN, GFP_KERNEL);
if (!block) {
pr_err("%s kmalloc failed\n", __func__);
return -ENOMEM;
}
memset(block, 0, count + BT_SDIO_HEAD_LEN);
memcpy(block + BT_SDIO_HEAD_LEN, buf, count);
down(&sem_id);
ret = sprdwcn_bus_list_alloc(BT_TX_CHANNEL, &tx_head, &tx_tail, &num);
if (ret) {
pr_err("%s sprdwcn_bus_list_alloc failed: %d\n", __func__, ret);
up(&sem_id);
return -ENOMEM;
}
tx_head->buf = block;
tx_head->len = count;
tx_head->next = NULL;
if (op == 0xfd09 || op == 0xfd0a || op == 0xfd0d) {
unsigned char tmp[10] = { 0 };
BT_VER("%s\n", __func__);
for (i = 0; i < count; i++) {
tmp[i % 10] = buf[i];
if (i % 10 == 9) {
BT_VER("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9]);
memset(tmp, 0, 10);
}
}
if (i % 10 < 9) {
BT_VER("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9]);
}
BT_VER("end %d \n", i);
}
BT_VERDUMP(block, count);
ret = sprdwcn_bus_push_list(BT_TX_CHANNEL, tx_head, tx_tail, num);
if (ret) {
pr_err("%s sprdwcn_bus_push_list failed: %d\n", __func__, ret);
kfree(tx_head->buf);
tx_head->buf = NULL;
sprdwcn_bus_list_free(BT_TX_CHANNEL, tx_head, tx_tail, num);
return -EBUSY;
}
if (buf[0] == 0x1) {
BT_DEBUG("%s ---\n", __func__);
}
/*
if (op == 0xfd09)
{
unsigned char woble_enable = buf[4];
unsigned char sleep_mod = buf[5];
if (woble_enable == 1 && sleep_mod == 1 || woble_enable == 0) //WOBLE_MOD_ENABLE, WOBLE_SLEEP_MOD_COULD_NOT_KNOW
{
set_need_indicate_cp2_woble(0);
pr_err("%s woble_enable %d sleep_mod %d\n", __func__, woble_enable, sleep_mod);
}
}
*/
return count;
}
// 04 05 04 00 00 02 13
void hci_add_device_to_wakeup_list_rtk(void);
void hci_disconnect(void);
unsigned char resume_rxmsg[7] = { 0x04, 0x05, 0x04, 0, 0x10, 0x0, 0x13 }; //
// unsigned char connect_event[22]={0x04,0x3e,0x13,0x01,0x00,0x10,0x00,0x00,0x00,0x20,0xec,0x8e,0xde,0xf3,0x0c,0x24,0x00,0x00,0x00,0xc8,0x00,0x00 };
extern unsigned char dis_flag;
int i = 0;
// int event_nu=10;
int list_time = 0x11;
extern int early_flag;
// extern int uni_sus_flag ;
// extern int uni_resu_flag;
extern int dis_cmd_flag;
extern unsigned char red_bt_flag; //区分蓝牙红外待机标志0x11:红外 0x22: bt
//extern int firs_dis_flag;
static int bt_tx_powerchange(int channel, int is_resume)
{
pr_err("%s channel %d is_resume %d need_indicate_cp2_woble %d", __func__, channel, is_resume, need_indicate_cp2_woble);
if (strcmp(WOBLE_TYPE, "disable")) {
unsigned long power_state = marlin_get_power_state();
pr_info("%s is_resume =%d", __func__, is_resume);
if (test_bit(MARLIN_BLUETOOTH, &power_state)) {
if (!is_resume) {
#ifdef UNISOC_AMLOGIC_SUSPEND
if (dis_cmd_flag == 0x11) {
dis_cmd_flag = 0x22;
early_flag = 0x22;
hci_disconnect();
pr_err("suspen_disconnect1\n");
}
#else
hci_disconnect();
pr_err("suspen_disconnect2\n");
#endif
//pr_err("pause_start\n");
#if 0
hci_cleanup_wakeup_list();
hci_add_device_to_wakeup_list_rtk();
hci_add_device_to_wakeup_list();
#endif
if (0x22 == red_bt_flag) //BT effect
{
hci_cleanup_wakeup_list();
hci_add_device_to_wakeup_list_rtk();
hci_add_device_to_wakeup_list();
hci_set_scan_parameters();
hci_set_scan_enable(1);
//pr_err("gy_0x__22");
} else { //red
hci_cleanup_wakeup_list();
hci_set_scan_enable(0);
}
hci_set_ap_sleep_mode(WOBLE_IS_NOT_SHUTDOWN, WOBLE_IS_NOT_RESUME);
hci_set_ap_start_sleep();
} else {
/*
unsigned char payload[4] = {0};
// int block_size = 4;
payload[0] = 0x04;
payload[1] = 0x10;
payload[2] = 0x01;
payload[3] = 0x00;
*/
#ifdef UNISOC_AMLOGIC_SUSPEND
hci_set_ap_sleep_mode(WOBLE_IS_NOT_SHUTDOWN, WOBLE_IS_RESUME);
hci_set_scan_enable(0);
// pr_err("resume_null1...\n"); //
#else
hci_set_ap_sleep_mode(WOBLE_IS_NOT_SHUTDOWN, WOBLE_IS_RESUME);
hci_set_scan_enable(0);
if ( 0x22 == red_bt_flag)
{
resume_transfer_upper(resume_rxmsg, 7);
}
pr_err("resume_null2...\n"); //
#endif
// bt_data_if_cb->recv(payload, block_size);
}
}
/*
if (!need_indicate_cp2_woble)
{
return 0;
}
if (BT_TX_CHANNEL != channel)
{
return -1;
}
if (!is_resume)
{
hci_woble_enable();
}
*/
}
return 0;
}
static struct mchn_ops_t bt_rx_ops = {
.channel = BT_RX_CHANNEL,
.hif_type = HW_TYPE_SDIO,
.inout = BT_RX_INOUT,
.pool_size = BT_RX_POOL_SIZE,
.pop_link = marlin_sdio_rx_cb,
};
static struct mchn_ops_t bt_tx_ops = {
.channel = BT_TX_CHANNEL,
.hif_type = HW_TYPE_SDIO,
.inout = BT_TX_INOUT,
.pool_size = BT_TX_POOL_SIZE,
.pop_link = marlin_sdio_tx_cb,
.power_notify = bt_tx_powerchange,
};
int marlin_sdio_init(struct bt_data_interface_cb_t* cb)
{
int ret = 0;
pr_err("%s\n", __func__);
ret = sprdwcn_bus_chn_init(&bt_rx_ops);
if (ret) {
pr_err("%s sprdwcn_bus_chn_init err chn %d\n", __func__, bt_rx_ops.channel);
}
ret = sprdwcn_bus_chn_init(&bt_tx_ops);
if (ret) {
pr_err("%s sprdwcn_bus_chn_init err chn %d\n", __func__, bt_tx_ops.channel);
}
bt_data_if_cb = cb;
sema_init(&sem_id, BT_TX_POOL_SIZE - 1);
return 0;
}
void marlin_sdio_cleanup(void)
{
sprdwcn_bus_chn_deinit(&bt_rx_ops);
sprdwcn_bus_chn_deinit(&bt_tx_ops);
}
static struct bt_data_interface_t marlin_sdio_interface = {
.size = sizeof(struct bt_data_interface_t),
.init = marlin_sdio_init,
.cleanup = marlin_sdio_cleanup,
.write = marlin_sdio_write
};
struct bt_data_interface_t* get_marlin_sdio_interface(void)
{
return &marlin_sdio_interface;
}