792 lines
19 KiB
C
Raw Permalink Normal View History

2025-05-10 21:58:58 +08:00
/*
* Copyright (C) 2015 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/console.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/kdev_t.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/tty.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/vt_kern.h>
#ifdef CONFIG_OF
#include <linux/of_device.h>
#endif
#include <linux/compat.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/tty_flip.h>
#include <linux/workqueue.h>
#include "include/debug.h"
#include "include/hci.h"
#include "include/interface.h"
#include "include/lpm.h"
#include "include/rfkill.h"
#include "include/sdio.h"
#include "include/sitm.h"
#include "include/tty.h"
#include <marlin_platform.h>
// #define UNISOC_AMLOGIC_SUSPEND
#ifdef UNISOC_AMLOGIC_SUSPEND
#include <linux/amlogic/pm.h> //add
static struct early_suspend bt_early_suspend; //add
#endif
#if 0
#define DO_SYSCALL_2(sc, t1, a1, t2, a2) \
(((asmlinkage long (*)(t1, t2))sys_call_table[__NR_##sc])(a1, a2));
#define USER_SYSCALL_2(sc, t1, a1, t2, a2) \
static inline asmlinkage long syscall_##sc(t1 a1, t2 a2) \
{ \
return DO_SYSCALL_2(sc, t1, a1, t2, a2) \
}
USER_SYSCALL_2(chmod, const char __user *, filename, mode_t, mode);
#endif
#ifndef USE_DTS
static void __exit uwe5621_bt_tty_exit(void);
#endif
static int mtty_remove(struct platform_device* pdev);
int closeflag = 0;
struct mtty_device* mtty_dev;
struct bt_data_interface_t* bt_data_interface;
static struct bt_data_interface_cb_t bt_data_interface_cb;
extern int list_time;
extern unsigned char dis_flag;
static int mtty_open(struct tty_struct* tty, struct file* filp)
{
struct mtty_device* mtty = NULL;
struct tty_driver* driver = NULL;
// start_marlin(MARLIN_BLUETOOTH);
bluetooth_set_power(0, 0);
clear_woble_devices();
if (tty == NULL) {
pr_err("mtty open input tty is NULL!\n");
return -ENOMEM;
}
driver = tty->driver;
mtty = (struct mtty_device*)driver->driver_state;
if (mtty == NULL) {
pr_err("mtty open input mtty NULL!\n");
return -ENOMEM;
}
mtty->tty = tty;
tty->driver_data = (void*)mtty;
atomic_set(&mtty->state, MTTY_STATE_OPEN);
sitm_init();
// pr_info("%s device success! pid %d %d %d sid %d\n", __func__, task_pid_nr(current), current->pid, pid_nr(get_task_pid(current, PIDTYPE_PID)),
// pid_nr(get_task_pid(current, PIDTYPE_SID)));
closeflag = 0;
list_time = 0x11;
dis_flag = 0;
return 0;
}
static void mtty_close(struct tty_struct* tty, struct file* filp)
{
closeflag = 1;
list_time = 0x11;
dis_flag = 0;
pr_info("mtty_close device success _close!\n");
/*
struct mtty_device *mtty = NULL;
if (tty == NULL)
{
pr_err("mtty close input tty is NULL!\n");
return;
}
mtty = (struct mtty_device *) tty->driver_data;
if (mtty == NULL)
{
pr_err("mtty close s tty is NULL!\n");
return;
}
atomic_set(&mtty->state, MTTY_STATE_CLOSE);
sitm_cleanup();
pr_info("mtty_close device success !\n");
//stop_marlin(MARLIN_BLUETOOTH);
bluetooth_set_power(0, 1);
*/
}
static inline int mtty_transfer_upper(const unsigned char* buf, int count)
{
int ret = tty_insert_flip_string(mtty_dev->port, buf, count);
if (ret != count) {
pr_err("%s err ret %d count %d !!!!!!!!!!!\n", __func__, ret, count);
}
tty_flip_buffer_push(mtty_dev->port);
return ret;
}
// add ..... 04 05 04 00 00 02 13
int resume_transfer_upper(const unsigned char* buf, int count)
{
int ret = tty_insert_flip_string(mtty_dev->port, buf, count);
pr_err("test_%s\n", __func__); //
if (ret != count) {
pr_err("%s err ret %d count %d !!!!!!!!!!!\n", __func__, ret, count);
}
tty_flip_buffer_push(mtty_dev->port);
return ret;
}
static int mtty_recv(const unsigned char* buf, int count)
{
// pr_err("mtty_recv count:%d\n", count);
// hex_dump_block(buf, count);
// return mtty_transfer_upper(buf, count);
return rx_data_recv(buf, count, mtty_transfer_upper);
}
static int sdio_data_transmit(uint8_t* data, size_t count)
{
return bt_data_interface->write(data, count);
}
static int mtty_write_plus(struct tty_struct* tty,
const unsigned char* buf, int count)
{
return sitm_write(buf, count, sdio_data_transmit);
}
static void mtty_flush_chars(struct tty_struct* tty)
{
}
static int mtty_write_room(struct tty_struct* tty)
{
return INT_MAX;
}
static const struct tty_operations mtty_ops = {
.open = mtty_open,
.close = mtty_close,
.write = mtty_write_plus,
.flush_chars = mtty_flush_chars,
.write_room = mtty_write_room,
};
static struct tty_port* mtty_port_init(void)
{
struct tty_port* port = NULL;
port = kzalloc(sizeof(struct tty_port), GFP_KERNEL);
if (port == NULL) {
return NULL;
}
tty_port_init(port);
return port;
}
static int mtty_tty_driver_init(struct mtty_device* device)
{
struct tty_driver* driver;
int ret = 0;
device->port = mtty_port_init();
if (!device->port) {
return -ENOMEM;
}
driver = alloc_tty_driver(MTTY_DEV_MAX_NR);
if (!driver) {
return -ENOMEM;
}
/*
* Initialize the tty_driver structure
* Entries in mtty_driver that are NOT initialized:
* proc_entry, set_termios, flush_buffer, set_ldisc, write_proc
*/
driver->owner = THIS_MODULE;
#ifndef USE_DTS
driver->driver_name = "ttyBT";
driver->name = "ttyBT";
#else
driver->driver_name = device->pdata->name;
driver->name = device->pdata->name;
#endif
#if 1
driver->major = 0;
#else
driver->major = TTYAUX_MAJOR;
driver->minor_start = 2;
#endif
driver->type = TTY_DRIVER_TYPE_SYSTEM;
driver->subtype = SYSTEM_TYPE_TTY;
driver->init_termios = tty_std_termios;
driver->driver_state = (void*)device;
device->driver = driver;
device->driver->flags = TTY_DRIVER_REAL_RAW;
/* initialize the tty driver */
tty_set_operations(driver, &mtty_ops);
tty_port_link_device(device->port, driver, 0);
ret = tty_register_driver(driver);
if (ret) {
put_tty_driver(driver);
tty_port_destroy(device->port);
return ret;
}
// sys_chmod("/dev/ttyBT0", 0777);
// syscall_chmod("/dev/ttyBT0", 0777);
return ret;
}
static void mtty_tty_driver_exit(struct mtty_device* device)
{
struct tty_driver* driver = device->driver;
tty_unregister_driver(driver);
put_tty_driver(driver);
tty_port_destroy(device->port);
}
static int mtty_parse_dt(struct mtty_init_data** init, struct device* dev)
{
#ifdef CONFIG_OF
struct device_node* np = dev->of_node;
struct mtty_init_data* pdata = NULL;
int ret;
pdata = kzalloc(sizeof(struct mtty_init_data), GFP_KERNEL);
if (!pdata) {
return -ENOMEM;
}
ret = of_property_read_string(np,
"sprd,name",
(const char**)&pdata->name);
if (ret) {
goto error;
}
*init = pdata;
return 0;
error:
kfree(pdata);
*init = NULL;
return ret;
#else
return -ENODEV;
#endif
}
static inline void mtty_destroy_pdata(struct mtty_init_data** init)
{
#if (defined CONFIG_OF) && (defined USE_DTS)
struct mtty_init_data* pdata = *init;
kfree(pdata);
*init = NULL;
#else
return;
#endif
}
static ssize_t at_store(struct device* dev,
struct device_attribute* attr,
const char* buf, size_t count)
{
pr_info("%s\n", __func__);
hci_woble_enable();
return count;
}
static ssize_t at_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
pr_info("%s\n", __func__);
return 0;
}
static ssize_t woble_set_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count)
{
unsigned char* p = (unsigned char*)buf;
unsigned char opcode = 0;
unsigned short handler = 0;
STREAM_TO_UINT8(opcode, p);
// pr_info("%s, len %lu op: %d\n", __func__, count, opcode);
switch (opcode) {
case WOBLE_KERNEL_OP_CLEAR:
clear_woble_devices();
break;
case WOBLE_KERNEL_OP_SET_OWN:
set_random_address(p);
break;
case WOBLE_KERNEL_OP_ADD_RMT: {
unsigned char type = 0;
STREAM_TO_UINT8(type, p);
STREAM_TO_UINT16(handler, p);
pr_info("%s, get type: %d\n", __func__, type);
update_woble_devices(type, handler, p);
} break;
case WOBLE_KERNEL_OP_DEL_RMT: {
unsigned char type = 0;
STREAM_TO_UINT8(type, p);
pr_info("%s, get type: %d\n", __func__, type);
del_woble_devices(type, p);
} break;
case WOBLE_KERNEL_OP_ENABLE_BLE_INDICATE: {
set_need_indicate_cp2_woble(1);
} break;
case WOBLE_KERNEL_OP_DISABLE_BLE_INDICATE: {
set_need_indicate_cp2_woble(0);
} break;
}
return count;
}
static ssize_t woble_set_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
pr_info("%s\n", __func__);
dump_woble_devices();
return 0;
}
static ssize_t ant_num_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
int num = 2;
num = marlin_get_ant_num();
pr_err("%s: %d", __func__, num);
return sprintf(buf, "%d", num);
}
static ssize_t chipid_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
int type = 0;
type = wcn_get_chip_model();
pr_err("%s: %d", __func__, type);
return sprintf(buf, "%d", type);
}
static ssize_t misc_node_store(struct device* dev,
struct device_attribute* attr,
const char* buf, size_t count)
{
unsigned char opcode = 0;
const char* tbuf = buf;
pr_info("%s\n", __func__);
if (memcmp("AT+BTLOG=", buf, strlen("AT+BTLOG=")) == 0 && count >= strlen("AT+BTLOG=") + 1) {
tbuf += strlen("AT+BTLOG=");
STREAM_TO_UINT8(opcode, tbuf);
set_log_level(opcode - '0');
} else if (memcmp("AT+SLEEP=", buf, strlen("AT+SLEEP=")) == 0 && count >= strlen("AT+SLEEP=") + 1) {
unsigned char is_resume = 0;
tbuf += strlen("AT+SLEEP=");
STREAM_TO_UINT8(is_resume, tbuf);
is_resume = is_resume - '0';
pr_err("%s AT+SLEEP=%d\n", __func__, is_resume);
switch (is_resume) {
case 0:
hci_set_scan_parameters();
hci_set_scan_enable(1);
break;
case 1:
hci_add_device_to_wakeup_list();
break;
case 2:
hci_set_ap_sleep_mode(WOBLE_IS_NOT_SHUTDOWN, WOBLE_IS_NOT_RESUME);
break;
case 3:
hci_set_ap_start_sleep();
break;
case 4:
hci_set_ap_sleep_mode(WOBLE_IS_NOT_SHUTDOWN, WOBLE_IS_RESUME);
break;
default:
break;
}
}
return count;
}
static ssize_t misc_node_show(struct device* dev,
struct device_attribute* attr,
char* buf)
{
pr_info("%s\n", __func__);
return 0;
}
#define ALL_PER 1
#if ALL_PER
#pragma push_macro("VERIFY_OCTAL_PERMISSIONS")
#ifdef VERIFY_OCTAL_PERMISSIONS
#undef VERIFY_OCTAL_PERMISSIONS
#endif
#define VERIFY_OCTAL_PERMISSIONS(perms) (perms)
#endif
#if ALL_PER
static DEVICE_ATTR(at, 00660, at_show, at_store);
static DEVICE_ATTR(woble_set, 00660, woble_set_show, woble_set_store);
static DEVICE_ATTR(ant_num, 00664, ant_num_show, 0);
static DEVICE_ATTR(chipid, 00664, chipid_show, 0);
static DEVICE_ATTR(misc_node, 00660, misc_node_show, misc_node_store);
#pragma pop_macro("VERIFY_OCTAL_PERMISSIONS")
#else
static DEVICE_ATTR(at, 00660, at_show, at_store);
static DEVICE_ATTR(woble_set, 00660, woble_set_show, woble_set_store);
static DEVICE_ATTR(ant_num, 00660, ant_num_show, 0);
static DEVICE_ATTR(chipid, 00660, chipid_show, 0);
static DEVICE_ATTR(misc_node, 00660, misc_node_show, misc_node_store);
#endif
struct attribute* bluetooth_attrs[] = {
&dev_attr_at.attr,
&dev_attr_woble_set.attr,
&dev_attr_ant_num.attr,
&dev_attr_chipid.attr,
&dev_attr_misc_node.attr,
NULL,
};
static struct attribute_group bluetooth_group = {
.name = NULL,
.attrs = bluetooth_attrs,
};
#ifdef UNISOC_AMLOGIC_SUSPEND
extern unsigned char resume_rxmsg[7];
int early_flag = 0x11;
int dis_cmd_flag = 0x11;
extern unsigned char red_bt_flag; //区分蓝牙红外待机标志0x11:红外 0x22: bt
//int uni_sus_flag =0x11;
//int uni_resu_flag= 0x11;
//int resume_suspen_flag=0x11;
//int firs_dis_flag=0x11;
static void bt_earlysuspend(struct early_suspend* h)
{
dis_cmd_flag = 0x11;
early_flag = 0x11;
}
static void bt_lateresume(struct early_suspend* h)
{
if (early_flag == 0x22) {
resume_transfer_upper(resume_rxmsg, 7);
}
early_flag = 0x11;
dis_cmd_flag = 0x11;
dis_flag =0 ;// 更新狀態
pr_err("unisoc_lateresume\n"); //
}
#endif
#ifdef CP2_RESET_SUPPORT
static void bt_cp2_pre_reset(void) {
pr_err("%s: entry!\n", __func__);
}
static void bt_cp2_post_reset(void) {
#define RESET_BUFSIZE 5
int ret = 0;
int block_size = RESET_BUFSIZE;
unsigned char reset_buf[RESET_BUFSIZE]= {0x04, 0xff, 0x02, 0x57, 0xa5};
pr_err("%s: reset callback coming\n", __func__);
if (mtty_dev != NULL) {
// if (!work_pending(&mtty_dev->bt_rx_work)) {
if (atomic_read(&mtty_dev->state) == MTTY_STATE_OPEN && (RESET_BUFSIZE > 0)) {
pr_err("%s tty_insert_flip_string", __func__);
while(ret < block_size){
pr_err("%s before tty_insert_flip_string ret: %d, len: %d\n",
__func__, ret, RESET_BUFSIZE);
ret = tty_insert_flip_string(mtty_dev->port,
(unsigned char *)reset_buf,
RESET_BUFSIZE); // -BT_SDIO_HEAD_LEN
pr_err("%s ret: %d, len: %d\n", __func__, ret, RESET_BUFSIZE);
if (ret)
tty_flip_buffer_push(mtty_dev->port);
block_size = block_size - ret;
ret = 0;
}
}
}
}
int bt_cp2_reset_callback(struct notifier_block *nb, unsigned long event, void *v) {
if (!v) {
pr_err("%s: v is NULL\n", __func__);
return NOTIFY_BAD;
}
pr_err("%s: %s %d\n", __func__, (char *)v, (int)event);
switch(event) {
case MARLIN_CP2_STS_ASSERTED:
bt_cp2_pre_reset();
break;
case MARLIN_CP2_STS_READY:
bt_cp2_post_reset();
break;
default:
pr_err("%s: parameter error event: %d\n", __func__, (int)event);
return NOTIFY_BAD;
}
return NOTIFY_OK;
}
static struct notifier_block bt_cp2_reset_notifier = {
.notifier_call = bt_cp2_reset_callback,
};
#endif /*CP2_RESET_SUPPORT*/
static int mtty_probe(struct platform_device* pdev)
{
struct mtty_init_data* pdata = (struct mtty_init_data*)pdev->dev.platform_data;
struct mtty_device* mtty;
int rval = 0;
if (marlin_get_wcn_module_vendor() < 0) {
pr_err("%s not unisoc soc, exit", __func__);
// mtty_remove(pdev);
// uwe5621_bt_tty_exit();
return -EACCES;
} else {
pr_err("%s unisoc soc, continue", __func__);
}
if (pdev->dev.of_node && !pdata) {
rval = mtty_parse_dt(&pdata, &pdev->dev);
if (rval) {
pr_err("%s failed to parse mtty device tree, ret=%d\n", __func__,
rval);
return rval;
}
}
mtty = kzalloc(sizeof(struct mtty_device), GFP_KERNEL);
if (mtty == NULL) {
mtty_destroy_pdata(&pdata);
pr_err("%s Failed to allocate device!\n", __func__);
return -ENOMEM;
}
mtty->pdata = pdata;
rval = mtty_tty_driver_init(mtty);
if (rval) {
mtty_tty_driver_exit(mtty);
kfree(mtty->port);
kfree(mtty);
mtty_destroy_pdata(&pdata);
pr_err("%s regitster notifier failed (%d)\n", __func__, rval);
return rval;
}
pr_info("%s init device addr: 0x%p\n", __func__, mtty);
platform_set_drvdata(pdev, mtty);
atomic_set(&mtty->state, MTTY_STATE_CLOSE);
mtty_dev = mtty;
if (sysfs_create_group(&(pdev->dev.kobj), &bluetooth_group)) {
pr_err("%s create dispc attr node failed", __func__);
}
rfkill_bluetooth_init(pdev);
// bluesleep_init();
// uwe5621_bt_tty_exit();
hci_init();
bt_data_interface_cb.size = sizeof(struct bt_data_interface_cb_t);
bt_data_interface_cb.recv = mtty_recv;
bt_data_interface = get_marlin_sdio_interface();
bt_data_interface->init(&bt_data_interface_cb);
/********************************************/
#ifdef UNISOC_AMLOGIC_SUSPEND
bt_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
bt_early_suspend.suspend = bt_earlysuspend;
bt_early_suspend.resume = bt_lateresume;
bt_early_suspend.param = pdev;
register_early_suspend(&bt_early_suspend);
#endif
/********************************************/
#ifdef CP2_RESET_SUPPORT
marlin_reset_callback_register(MARLIN_BLUETOOTH, &bt_cp2_reset_notifier);
#endif /*CP2_RESET_SUPPORT*/
return 0;
}
static int mtty_remove(struct platform_device* pdev)
{
struct mtty_device* mtty = platform_get_drvdata(pdev);
rfkill_bluetooth_remove(pdev);
pr_err("%s\n", __func__);
mtty_tty_driver_exit(mtty);
bt_data_interface->cleanup();
kfree(mtty->port);
mtty_destroy_pdata(&mtty->pdata);
/* tasklet_kill(&mtty->rx_task); */
kfree(mtty);
platform_set_drvdata(pdev, NULL);
// bluesleep_exit();
hci_destory();
return 0;
}
static int mtty_suspend(struct platform_device* device, pm_message_t state)
{
pr_err("%s\n", __func__);
// at_store(0, 0, 0, 0);
// stop_marlin(MARLIN_BLUETOOTH);
// sprdwcn_bus_remove_card();
return 0;
}
static void mtty_shutdown(struct platform_device* device)
{
pr_err("%s\n", __func__);
return;
}
static int mtty_resume(struct platform_device* device)
{
pr_err("%s\n", __func__);
return 0;
}
static void mtty_device_release(struct device* device)
{
pr_err("%s\n", __func__);
return;
}
static struct platform_device uwe5621_bt_tty_device = {
.name = "mtty",
.dev = {
.release = mtty_device_release,
},
};
static const struct of_device_id mtty_match_table[] = {
{
.compatible = "sprd,mtty",
},
};
static struct platform_driver uwe5621_bt_tty_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "mtty",
.of_match_table = mtty_match_table,
},
.probe = mtty_probe,
.remove = mtty_remove,
.suspend = mtty_suspend,
.resume = mtty_resume,
.shutdown = mtty_shutdown,
};
#ifdef USE_DTS
// for dts
module_platform_driver(uwe5621_bt_tty_driver);
#else
static int __init uwe5621_bt_tty_init(void)
{
pr_err("%s\n", __func__);
platform_device_register(&uwe5621_bt_tty_device);
return platform_driver_register(&uwe5621_bt_tty_driver);
}
static void __exit uwe5621_bt_tty_exit(void)
{
pr_err("%s\n", __func__);
platform_driver_unregister(&uwe5621_bt_tty_driver);
platform_device_unregister(&uwe5621_bt_tty_device);
}
// module_init(uwe5621_bt_tty_init);
late_initcall_sync(uwe5621_bt_tty_init);
module_exit(uwe5621_bt_tty_exit);
#endif
MODULE_AUTHOR("Unisoc wcn bt");
MODULE_DESCRIPTION("Unisoc marlin tty driver");