/* * * Seekwave Bluetooth driver * * Copyright (C) 2023 Seekwave Tech Ltd. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #include #include #include "skw_btsnoop.h" #include "skw_log.h" #include "skw_common.h" #define VERSION "0.1" enum { BT_STATE_DEFAULT = 0x00, BT_STATE_CLOSE, BT_STATE_REMOVE }; int skwbt_log_disable = 0; int is_init_mode = 0; uint16_t chip_version = 0; static wait_queue_head_t nv_wait_queue; static wait_queue_head_t recovery_wait_queue; static wait_queue_head_t close_wait_queue; Wakeup_ADV_Info_St wakeup_adv_info = {0}; char *bd_addr = NULL; static atomic_t evt_recv; static atomic_t cmd_reject; static atomic_t atomic_close_sync;//make sure running close func before remove func module_param(bd_addr, charp, S_IRUSR); static int btseekwave_send_frame(struct hci_dev *hdev, struct sk_buff *skb); int btseekwave_plt_event_notifier(struct notifier_block *nb, unsigned long action, void *param); int btseekwave_send_hci_command(struct hci_dev *hdev, u16 opcode, int len, char *cmd_pld); extern int skw_start_bt_service(void); extern int skw_stop_bt_service(void); struct btseekwave_data { struct hci_dev *hdev; struct sv6160_platform_data *pdata; struct work_struct work; struct notifier_block plt_notifier; uint8_t plt_notifier_set; uint8_t bt_is_open; struct sk_buff_head cmd_txq; struct sk_buff_head data_txq; struct sk_buff_head audio_txq; }; struct btseekwave_data *skw_data = NULL; void btseekwave_hci_hardware_error(struct hci_dev *hdev) { struct sk_buff *skb = NULL; int len = 3; uint8_t hw_err_pkt[4] = {HCI_EVENT_PKT, HCI_EVT_HARDWARE_ERROR, 0x01, 0x00}; uint8_t *base_ptr = NULL; skb = alloc_skb(len, GFP_ATOMIC); if (!skb) { SKWBT_ERROR("%s: failed to allocate mem", __func__); return; } base_ptr = (uint8_t *)skb_put(skb, len); if(base_ptr)//for Coverity scan { memcpy(base_ptr, hw_err_pkt + 1, len); } bt_cb(skb)->pkt_type = HCI_EVENT_PKT; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) hci_recv_frame(hdev, skb); #else hci_recv_frame(skb); #endif SKWBT_INFO("%s enter", __func__); } static int btseekwave_tx_packet(int portno, struct btseekwave_data *data, struct sk_buff *skb) { int err = 0; u32 *d; uint32_t pkt_len = skb->len; d = (u32 *)skb->data; //SKWBT_INFO("%s enter size %d: 0x%x 0x%x\n", __func__, skb->len, d[0], d[1]); if(data->pdata && data->pdata->hw_sdma_tx) { err = data->pdata->hw_sdma_tx(portno, skb->data, skb->len); } if(err < 0) { SKWBT_ERROR("btseekwave_tx_packet tx failed err:%d, pkt_len:%d", err, pkt_len); return err; } kfree_skb(skb); data->hdev->stat.byte_tx += pkt_len; //SKWBT_INFO("%s, pkt:%d, users:%d \n", __func__, bt_cb((skb))->pkt_type, skb->users.refs.counter); return 0; } static void btseekwave_work(struct work_struct *work) { struct btseekwave_data *data = container_of(work, struct btseekwave_data, work); struct sk_buff *skb; int err = 0; //SKWBT_INFO("%s %s", __func__, data->hdev->name); if(atomic_read(&cmd_reject)) { return ; } while ((skb = skb_dequeue(&data->cmd_txq))) { err = btseekwave_tx_packet(data->pdata->cmd_port, data, skb); if (err < 0) { data->hdev->stat.err_tx++; skb_queue_head(&data->cmd_txq, skb); SKWBT_ERROR("btseekwave_tx_packet command failed len: %d\n", err); break; } } while (err >= 0 && (skb = skb_dequeue(&data->data_txq))) { err = btseekwave_tx_packet(data->pdata->data_port, data, skb); if (err < 0) { data->hdev->stat.err_tx++; skb_queue_head(&data->data_txq, skb); SKWBT_ERROR("btseekwave_tx_packet data failed len: %d\n", err); break; } } while (err >= 0 && (skb = skb_dequeue(&data->audio_txq))) { err = btseekwave_tx_packet(data->pdata->audio_port, data, skb); if (err < 0) { data->hdev->stat.err_tx++; skb_queue_head(&data->audio_txq, skb); SKWBT_ERROR("btseekwave_tx_packet audio failed len: %d\n", err); break; } } // SKWBT_INFO("btseekwave_work done\n"); } static int btseekwave_rx_packet(struct btseekwave_data *data, u8 pkt_type, void *buf, int c_len) { struct sk_buff *skb; //SKWBT_INFO("rx hci pkt len = %d, pkt_type:%d", c_len, pkt_type); skb = bt_skb_alloc(c_len, GFP_ATOMIC); if (!skb) { SKWBT_ERROR("skwbt alloc skb failed, len: %d\n", c_len); return 0; } bt_cb((skb))->expect = 0; skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = pkt_type; memcpy(skb_put(skb, c_len), buf, c_len); #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) hci_recv_frame(data->hdev, skb); #else hci_recv_frame(skb); #endif return 0; } int btseekwave_rx_complete(int portno, struct scatterlist *priv, int size, void *buf) { int ret = 0; struct btseekwave_data *data = (struct btseekwave_data *)priv; u8 pkt_type = 0; //SKWBT_INFO("btseekwave_rx_complete size=%d, is_init_mode:%d, data:0x%04X", size, is_init_mode, buf ? (*(uint32_t *)buf) : 0xFFFFFFFF); if(size == 0) { return 0; } else if(size < 0)//CP assert/exception { SKWBT_ERROR("cp exception\n"); return 0; } #if SKWBT_LOG_PORT_EN skwbt_log_port_data_write(1, (uint8_t *)buf, (uint16_t)size); #endif pkt_type = *((u8 *)buf); if(HCI_EVENT_SKWLOG == pkt_type) { #if BT_CP_LOG_EN skwlog_write(buf, size); #endif return 0; } if((HCI_EVENT_PKT == pkt_type) || (HCI_ACLDATA_PKT == pkt_type) || (HCI_SCODATA_PKT == pkt_type)) { #if BT_HCI_LOG_EN skw_btsnoop_capture(buf, 1); #endif if(is_init_mode)//command complete event { hci_cmd_cmpl_evt_st *hci_evt = (hci_cmd_cmpl_evt_st *)buf; if((HCI_EVENT_PKT == pkt_type) && (HCI_COMMAND_COMPLETE_EVENT == hci_evt->evt_op) && (HCI_CMD_READ_LOCAL_VERSION_INFO == hci_evt->cmd_op)) { struct hci_rp_read_local_version *ver; ver = (struct hci_rp_read_local_version *)(buf + 6); chip_version = le16_to_cpu(ver->hci_rev); SKWBT_INFO("%s, chip version:0x%X", __func__, chip_version); } atomic_inc(&evt_recv); wake_up(&nv_wait_queue); SKWBT_INFO("init cmd response: 0x%x \n", *((u32 *)(buf + 3))); return 0; } ret = btseekwave_rx_packet(data, pkt_type, buf + 1, size - 1); } else { SKWBT_ERROR("err hci packet: %x, len:%d\n", pkt_type, size); } return ret; } struct sk_buff *btseekwave_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param) { int len = HCI_COMMAND_HDR_SIZE + plen; struct hci_command_hdr *hdr; struct sk_buff *skb; skb = bt_skb_alloc(len, GFP_ATOMIC); if (!skb) { return NULL; } hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); hdr->opcode = cpu_to_le16(opcode); hdr->plen = plen; if (plen) { uint8_t *base_ptr = (uint8_t *)skb_put(skb, plen); if(base_ptr)//for Coverity scan { memcpy(base_ptr, param, plen); } } bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; return skb; } void btseekwave_write_bd_addr(struct hci_dev *hdev) { u8 cmd_pld[32] = {0x00}; //struct sk_buff *skb; int ret; if(bd_addr) { uint8_t i = 0, j, size = skw_strlen(bd_addr); SKWBT_INFO("%s bd addr:%s", __func__, bd_addr); //BC:9A:98:86:74:62 if(size != 17) { return ; } for (i = 16, j = 0; j < 6; i -= 3, j++) { cmd_pld[j] = (skw_char2hex(bd_addr[i - 1]) << 4) | skw_char2hex(bd_addr[i]); } } else { if(!skw_get_bd_addr(cmd_pld))//bd addr is invalid { return ; } } ret = btseekwave_send_hci_command(hdev, HCI_CMD_WRITE_BD_ADDR, BD_ADDR_LEN, cmd_pld); if(ret < 0) { SKWBT_ERROR("%s write bd_addr timeout", __func__); } } /* 0: success other:fail */ int btseekwave_send_hci_command(struct hci_dev *hdev, u16 opcode, int len, char *cmd_pld) { struct sk_buff *skb; int ret = 0, i; skb = btseekwave_prepare_cmd(hdev, opcode, len, cmd_pld); if(!skb) { SKWBT_ERROR("%s no memory for command", __func__); return -1; } //waiting controller response atomic_set(&evt_recv, 0); ret = btseekwave_send_frame(hdev, skb); if(ret != 0) { SKWBT_ERROR("%s cmd send fail, ret:%d", __func__, ret); return -1; } for(i = 0; i < 3; i++) { ret = wait_event_timeout(nv_wait_queue, (atomic_read(&evt_recv)), msecs_to_jiffies(1000)); if((ret > 0) || (atomic_read(&evt_recv))) { return 0; } SKWBT_INFO("%s cp response timeout, ret:%d", __func__, ret); if(ret == 0)//timeout { break; } } return -1; } void btseekwave_write_ble_wakeup_adv_info(struct hci_dev *hdev) { uint8_t adv_data_len = wakeup_adv_info.data_len; if(adv_data_len > 0) { uint8_t p_buf[256] = {0x00}; uint8_t *ptr = p_buf; uint8_t i, adv_len; uint8_t pld_len = adv_data_len + 4;//add the length of gpio & level & grp nums & total len Wakeup_ADV_Grp_St *adv_grp; UINT8_TO_STREAM(ptr, wakeup_adv_info.gpio_no); UINT8_TO_STREAM(ptr, wakeup_adv_info.level); UINT8_TO_STREAM(ptr, wakeup_adv_info.grp_nums); UINT8_TO_STREAM(ptr, adv_data_len); for(i = 0; i < wakeup_adv_info.grp_nums; i++) { adv_grp = &wakeup_adv_info.adv_group[i]; UINT8_TO_STREAM(ptr, adv_grp->grp_len); UINT8_TO_STREAM(ptr, adv_grp->addr_offset); adv_len = (adv_grp->grp_len - 2) >> 1; SKWBT_INFO("grp len:%d, adv_len:%d", adv_grp->grp_len, adv_len); memcpy(ptr, adv_grp->data, adv_len); ptr += adv_len; memcpy(ptr, adv_grp->mask, adv_len); ptr += adv_len; } btseekwave_send_hci_command(hdev, HCI_CMD_WRITE_WAKEUP_ADV_DATA, pld_len, p_buf); } } void btseekwave_write_ble_wakeup_adv_enable(le_wakeup_op_enum enable_op) { if((wakeup_adv_info.data_len > 0) && skw_data && (skw_data->bt_is_open)) { char buffer[2] = {0x00}; struct sk_buff *skb; struct hci_dev *hdev = skw_data->hdev; buffer[0] = enable_op; skb = btseekwave_prepare_cmd(hdev, HCI_CMD_WRITE_WAKEUP_ADV_ENABLE_PLT, 1, buffer); if(!skb) { SKWBT_ERROR("%s no memory for nv command", __func__); return ; } SKWBT_INFO("%s", __func__); btseekwave_send_frame(hdev, skb); msleep(5); } } EXPORT_SYMBOL_GPL(btseekwave_write_ble_wakeup_adv_enable); static void btseekwave_port_close(struct btseekwave_data *data) { if(data && data->pdata) { data->bt_is_open = 0; if(data->pdata->modem_unregister_notify && data->plt_notifier_set) { data->plt_notifier_set = 0; data->pdata->modem_unregister_notify(&data->plt_notifier); } #if INCLUDE_NEW_VERSION if(data->pdata->service_stop) { data->pdata->service_stop(); } else { SKWBT_ERROR("func %s service_stop not exist", __func__); } #else skw_stop_bt_service(); #endif if(data->pdata->close_port) { data->pdata->close_port(data->pdata->cmd_port); if(data->pdata->data_port != 0) { data->pdata->close_port(data->pdata->data_port); } if(data->pdata->audio_port != 0) { data->pdata->close_port(data->pdata->audio_port); } } } } int btseekwave_download_nv(struct hci_dev *hdev) { int page_offset = 0, ret = 0, len = 0; u8 *cmd_pld = NULL; const struct firmware *fw; int err = 0, count = 0; uint8_t log_disable = 1, cp_log_disable = 1; SKWBT_INFO("%s", __func__); is_init_mode = 1; chip_version = SKW_CHIPID_6160; ret = btseekwave_send_hci_command(hdev, HCI_CMD_READ_LOCAL_VERSION_INFO, 0, NULL); if(ret < 0) { SKWBT_ERROR("%s, read local version err", __func__); if(skw_data) { skw_data->pdata->modem_assert(); } return -1; } if(SKW_CHIPID_6316 == chip_version) { err = request_firmware(&fw, NV_FILE_NAME_6316, &hdev->dev); } else if(SKW_CHIPID_6160_LITE == chip_version) { err = request_firmware(&fw, NV_FILE_NAME_6160_LITE, &hdev->dev); } else { err = request_firmware(&fw, NV_FILE_NAME, &hdev->dev); } if (err < 0) { SKWBT_ERROR("nv file load fail, BT Controller Version:0x%04X", chip_version); return err; } cmd_pld = (u8 *)kzalloc(512, GFP_KERNEL); if(cmd_pld == NULL) { SKWBT_ERROR("%s malloc fail", __func__); release_firmware(fw); return -1; } #if ((BT_CP_LOG_EN == 1) || (BT_HCI_LOG_EN == 1)) skwbt_log_disable = 0; #endif #if BT_CP_LOG_EN cp_log_disable = 0; #endif if((SKW_CHIPID_6316 == chip_version) || (SKW_CHIPID_6160_LITE == chip_version)) { int total_len = 0; int nv_pkt_len = 0; uint8_t nv_tag = 0; uint8_t *base_ptr = NULL; count = 4;//skip header while(count < fw->size) { nv_tag = fw->data[count]; nv_pkt_len = fw->data[count + 2] + 3; if((nv_pkt_len + total_len) >= NV_FILE_RD_BLOCK_SIZE) { cmd_pld[0] = (char)page_offset; cmd_pld[1] = (char)total_len;//para len ret = btseekwave_send_hci_command(hdev, HCI_CMD_SKW_BT_NVDS, total_len + 2, cmd_pld); if(ret < 0) { //return -1; total_len = 0; err = -1; break; } page_offset ++; total_len = 0; continue; } base_ptr = cmd_pld + 2 + total_len; if(base_ptr) { memcpy(base_ptr, fw->data + count, nv_pkt_len); } if(nv_tag == NV_TAG_DSP_LOG_SETTING) { log_disable = fw->data[count + 3]; if(cp_log_disable) { log_disable = 1; } if(total_len < NV_FILE_RD_BLOCK_SIZE)//for Coverity scan { *(cmd_pld + 2 + total_len + 3) = log_disable; } SKWBT_INFO("%s log_disable from NV:%d, skwbt_log_disable:%d", __func__, log_disable, cp_log_disable); } count += nv_pkt_len; total_len += nv_pkt_len; } if(total_len > 0) { cmd_pld[0] = (char)page_offset; cmd_pld[1] = (char)total_len;//para len ret = btseekwave_send_hci_command(hdev, HCI_CMD_SKW_BT_NVDS, total_len + 2, cmd_pld); if(ret < 0) { SKWBT_ERROR("%s, line:%d, cp response timeout", __func__, __LINE__); } } } else { log_disable = fw->data[0x131]; SKWBT_INFO("%s log_disable from NV:%d, skwbt_log_disable:%d", __func__, log_disable, cp_log_disable); while(count < fw->size) { len = NV_FILE_RD_BLOCK_SIZE; if((fw->size - count) < NV_FILE_RD_BLOCK_SIZE) { len = fw->size - count; } cmd_pld[0] = (char)page_offset; cmd_pld[1] = (char)len;//para len memcpy(cmd_pld + 2, fw->data + count, len); count += len; if(1 == page_offset) { if(cp_log_disable) { log_disable = 1; } *(cmd_pld + 2 + 53) = log_disable; } ret = btseekwave_send_hci_command(hdev, HCI_CMD_SKW_BT_NVDS, len + 2, cmd_pld); if(ret < 0) { SKWBT_ERROR("%s, line:%d, cp response timeout", __func__, __LINE__); break; } page_offset ++; } } if(err == 0) { btseekwave_write_bd_addr(hdev); btseekwave_write_ble_wakeup_adv_info(hdev); } kfree(cmd_pld); release_firmware(fw); is_init_mode = 0; return err; } static int btseekwave_open(struct hci_dev *hdev) { struct btseekwave_data *data = hci_get_drvdata(hdev); int err = -1; SKWBT_INFO("%s enter...\n", __func__); if(atomic_read(&cmd_reject)) { int ret = wait_event_timeout(recovery_wait_queue, (!atomic_read(&cmd_reject)), msecs_to_jiffies(2000)); if(!ret) { SKWBT_ERROR("%s timeout", __func__); return ret; } } if(data && data->pdata && data->pdata->open_port) { SKWBT_INFO("%s, cmd_port:%d, mode data_port:%d, audio_port:%d\n", __func__, data->pdata->cmd_port, data->pdata->data_port, data->pdata->audio_port); err = data->pdata->open_port(data->pdata->cmd_port, btseekwave_rx_complete, data); if(err < 0) { SKWBT_ERROR("command port open fail, ret:%d", err); return err; } if(data->pdata->data_port != 0) { err = data->pdata->open_port(data->pdata->data_port, btseekwave_rx_complete, data); if(err < 0) { SKWBT_ERROR("data port open fail, ret:%d", err); return err; } } if(data->pdata->audio_port != 0) { err = data->pdata->open_port(data->pdata->audio_port, btseekwave_rx_complete, data); if(err < 0) { SKWBT_ERROR("audio port open fail, ret:%d", err); return err; } } #if INCLUDE_NEW_VERSION if(data->pdata->service_start) { err = data->pdata->service_start(); if(err != 0) { SKWBT_ERROR("func %s service_start err:%d", __func__, err); return err; } } else { SKWBT_ERROR("func %s service_start not exist", __func__); return -1; } #else err = skw_start_bt_service(); if(err != 0) { SKWBT_ERROR("%s service_start err:%d", __func__, err); return err; } #endif err = btseekwave_download_nv(hdev); if(err == 0) { data->bt_is_open = 1; if(data->plt_notifier_set == 0) { data->plt_notifier.notifier_call = btseekwave_plt_event_notifier; data->pdata->modem_register_notify(&data->plt_notifier); data->plt_notifier_set = 1; } } else { btseekwave_port_close(data); } } atomic_set(&atomic_close_sync, 0); return err; } void btseekwave_write_bt_state(struct hci_dev *hdev) { //char buffer[10] = {0x01, 0x80, 0xFE, 0x01, 0x00}; u8 cmd_pld[5] = {0x00}; struct sk_buff *skb = btseekwave_prepare_cmd(hdev, HCI_CMD_WRITE_BT_STATE, 1, cmd_pld); if(skb) { btseekwave_send_frame(hdev, skb); msleep(15); } } static int btseekwave_close(struct hci_dev *hdev) { struct btseekwave_data *data = hci_get_drvdata(hdev); int state = 0; SKWBT_INFO("%s enter...\n", __func__); if(data && (data->pdata->data_port == 0)) { #if INCLUDE_NEW_VERSION #else btseekwave_write_bt_state(hdev); #endif } if(atomic_read(&cmd_reject)) { int ret = wait_event_timeout(recovery_wait_queue, (!atomic_read(&cmd_reject)), msecs_to_jiffies(2000)); if(!ret) { SKWBT_ERROR("%s timeout, ret:%d", __func__, ret); } } btseekwave_port_close(data); state = atomic_read(&atomic_close_sync); SKWBT_INFO("func %s, atomic_read:%d", __func__, state); if(state == BT_STATE_DEFAULT) { atomic_set(&atomic_close_sync, BT_STATE_CLOSE); } else { atomic_set(&atomic_close_sync, BT_STATE_CLOSE); wake_up(&close_wait_queue); } return 0; } static int btseekwave_flush(struct hci_dev *hdev) { struct btseekwave_data *data = hci_get_drvdata(hdev); SKWBT_INFO("%s", hdev->name); if (work_pending(&data->work)) { cancel_work_sync(&data->work); } skb_queue_purge(&data->cmd_txq); skb_queue_purge(&data->data_txq); skb_queue_purge(&data->audio_txq); return 0; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) // #else /*for low version*/ static int btseekwave_send_frame_lv(struct sk_buff *skb) { struct hci_dev *hdev = (struct hci_dev *) skb->dev; return btseekwave_send_frame(hdev, skb); } #endif static int btseekwave_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { struct btseekwave_data *data = hci_get_drvdata(hdev); u8 pkt_type = bt_cb(skb)->pkt_type; u8 *d = skb_push(skb, 1); *d = pkt_type; if(data->pdata == NULL) { SKWBT_ERROR("%s pointer is null", __func__); return -EILSEQ; } if((pkt_type == HCI_COMMAND_PKT) || ((pkt_type == HCI_ACLDATA_PKT) && (data->pdata->data_port == 0)) || ((pkt_type == HCI_SCODATA_PKT) && (data->pdata->audio_port == 0))) { hdev->stat.cmd_tx++; skb_queue_tail(&data->cmd_txq, skb); } else if(pkt_type == HCI_ACLDATA_PKT) { hdev->stat.acl_tx++; skb_queue_tail(&data->data_txq, skb); } else if(pkt_type == HCI_SCODATA_PKT) { skb_queue_tail(&data->audio_txq, skb); hdev->stat.sco_tx++; } else { return -EILSEQ; } #if BT_HCI_LOG_EN skw_btsnoop_capture(skb->data, 0); #endif #if SKWBT_LOG_PORT_EN skwbt_log_port_data_write(0, skb->data, (uint16_t)skb->len); #endif schedule_work(&data->work); return 0; } static int btseekwave_setup(struct hci_dev *hdev) { SKWBT_INFO("%s", __func__); return 0; } /* must be in DEVICE_ASSERT_EVENT to DEVICE_DUMPDONE_EVENT closing USB */ int btseekwave_plt_event_notifier(struct notifier_block *nb, unsigned long action, void *param) { SKWBT_INFO("%s, action:%d", __func__, (int)action); if(skw_data == NULL) { return 0; } #if 1 switch(action) { case DEVICE_ASSERT_EVENT: { //make surce host data cann't send to plt driver before close usb atomic_set(&cmd_reject, 1); #if INCLUDE_NEW_VERSION if((skw_data) && (skw_data->pdata) && (skw_data->pdata->service_stop)) { skw_data->pdata->service_stop(); } else { SKWBT_ERROR("func %s service_stop not exist", __func__); } #else skw_stop_bt_service(); #endif } break; case DEVICE_BSPREADY_EVENT:// { if(atomic_read(&cmd_reject)) { struct btseekwave_data *data = skw_data;//container_of(nb, struct btseekwave_data, plt_notifier); atomic_set(&cmd_reject, 0); wake_up(&recovery_wait_queue); if(data) { btseekwave_flush(data->hdev); btseekwave_hci_hardware_error(data->hdev);//report to host } } } break; case DEVICE_DUMPDONE_EVENT: { } break; case DEVICE_BLOCKED_EVENT: { } break; default: { } break; } #endif return NOTIFY_OK; } static int btseekwave_probe(struct platform_device *pdev) { struct btseekwave_data *data; struct device *dev = &pdev->dev; struct sv6160_platform_data *pdata = dev->platform_data; struct hci_dev *hdev; int err; if(pdata == NULL) { SKWBT_ERROR("%s pdata is null", __func__); return -ENOMEM; } SKWBT_INFO("%s pdev name %s\n", __func__, pdata->port_name); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) { SKWBT_ERROR("%s alloc fail", __func__); return -ENOMEM; } skw_data = data; data->plt_notifier_set = 0; data->bt_is_open = 0; data->pdata = pdata; INIT_WORK(&data->work, btseekwave_work); skb_queue_head_init(&data->cmd_txq); skb_queue_head_init(&data->data_txq); skb_queue_head_init(&data->audio_txq); hdev = hci_alloc_dev(); if (!hdev) { return -ENOMEM; } hdev->bus = HCI_SDIO; hci_set_drvdata(hdev, data); data->hdev = hdev; SET_HCIDEV_DEV(hdev, dev); hdev->open = btseekwave_open; hdev->close = btseekwave_close; hdev->flush = btseekwave_flush; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) hdev->send = btseekwave_send_frame; #else hdev->send = btseekwave_send_frame_lv; #endif hdev->setup = btseekwave_setup; atomic_set(&hdev->promisc, 0); //set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); //set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks); err = hci_register_dev(hdev); if (err < 0) { hci_free_dev(hdev); return err; } platform_set_drvdata(pdev, data); skw_bd_addr_gen_init(); atomic_set(&cmd_reject, 0); atomic_set(&atomic_close_sync, BT_STATE_DEFAULT); #if SKWBT_LOG_PORT_EN skwbt_log_port_init(); skwbt_log_port_set_pdata(pdata); #endif return 0; } static int btseekwave_remove(struct platform_device *pdev) { int state = atomic_read(&atomic_close_sync); SKWBT_INFO("func %s, atomic_read:%d", __func__, state); atomic_set(&cmd_reject, 0); if(BT_STATE_DEFAULT == state) { int ret; atomic_set(&atomic_close_sync, BT_STATE_REMOVE); ret = wait_event_timeout(close_wait_queue, (BT_STATE_CLOSE == atomic_read(&atomic_close_sync)), msecs_to_jiffies(500)); if(!ret) { SKWBT_ERROR("%s timeout, ret:%d", __func__, ret); } } atomic_set(&atomic_close_sync, BT_STATE_DEFAULT); skw_data = NULL; if(pdev) { struct btseekwave_data *data = platform_get_drvdata(pdev); struct hci_dev *hdev; if (!data) { return 0; } hdev = data->hdev; data->bt_is_open = 0; if(data->pdata && data->pdata->modem_unregister_notify && data->plt_notifier_set) { SKWBT_INFO("func %s modem_unregister_notify", __func__); data->pdata->modem_unregister_notify(&data->plt_notifier); data->plt_notifier_set = 0; } btseekwave_flush(hdev); platform_set_drvdata(pdev, NULL); hci_unregister_dev(hdev); hci_free_dev(hdev); #if SKWBT_LOG_PORT_EN skwbt_log_port_exit(); #endif } SKWBT_INFO("func %s end", __func__); return 0; } static struct platform_driver btseekwave_driver = { .driver = { .name = (char *)"btseekwave", .bus = &platform_bus_type, .pm = NULL, }, .probe = btseekwave_probe, .remove = btseekwave_remove, }; int btseekwave_init(void) { SKWBT_INFO("Seekwave Bluetooth driver ver %s\n", VERSION); init_waitqueue_head(&nv_wait_queue); init_waitqueue_head(&recovery_wait_queue); init_waitqueue_head(&close_wait_queue); atomic_set(&evt_recv, 0); wakeup_adv_info.data_len = 0; #ifdef BLE_WAKEUP_ADV_INFO skw_parse_wakeup_adv_conf(BLE_WAKEUP_ADV_INFO, &wakeup_adv_info); #endif #if BT_HCI_LOG_EN skw_btsnoop_init(); #endif #if BT_CP_LOG_EN skwlog_init(); #endif return platform_driver_register(&btseekwave_driver); } void btseekwave_exit(void) { #if BT_HCI_LOG_EN skw_btsnoop_close(); #endif #if BT_CP_LOG_EN skwlog_close(); #endif platform_driver_unregister(&btseekwave_driver); } module_init(btseekwave_init); module_exit(btseekwave_exit); MODULE_DESCRIPTION("Seekwave Bluetooth driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL");