2025-05-10 21:58:58 +08:00

337 lines
9.7 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
}