/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF #include #endif #include #include #include #include #include #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 // #define UNISOC_AMLOGIC_SUSPEND #ifdef UNISOC_AMLOGIC_SUSPEND #include //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");