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