#include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PROPERTY #include #endif #if defined(CONFIG_BT_QC_TEST) || defined(CONFIG_BT_CTRL_RF_DEBUG) #define BQB_USE_UART_0 0 #else #define BQB_USE_UART_0 1 #endif #define BT_HCI_OP_VS_WRITE_BB_REG 0xfc8a #define BT_HCI_OP_VS_READ_BB_REG 0xfc8b #define BT_HCI_OP_VS_WRITE_RF_REG 0xfc8c #define BT_HCI_OP_VS_READ_RF_REG 0xfc8d #define BT_HCI_EVT_VS_READ_BB_REG_REPORT 0x80 #define BT_HCI_EVT_VS_READ_RF_REG_REPORT 0x81 #define BT_HCI_OP_VS_SET_APLL_TEMP_COMP 0xfc30 #define BT_HCI_OP_VS_DO_APLL_TEMP_COMP 0xfc31 struct bqb_bb_reg_report { uint32_t base_addr; uint32_t size; uint32_t result[0]; } __packed; struct bqb_rf_reg_report { uint16_t base_addr; uint16_t size; uint16_t result[0]; } __packed; #if BQB_USE_UART_0 #define BQB_UART_STA UART0_STA #define BQB_UART_SET_BR 115200 #define BLE_BQB_UART "UART_0" static struct device *uart_dev; #else #define BQB_UART_STA UART1_STA #define BLE_BQB_UART "UART_1" static struct device *uart_dev; #endif struct acts_bqb_data { uint8_t tx_data[128]; uint8_t rx_data[128]; uint8_t rx_type; uint8_t bqb_mode; uint8_t bqb_in_test; }; static struct acts_bqb_data *bqb_info; static void bqb_vs_evt_handle(uint8_t *buf) { int i; struct bqb_bb_reg_report *bb_evt; struct bqb_rf_reg_report *rf_evt; uint8_t subevt = buf[0]; buf++; if (subevt == BT_HCI_EVT_VS_READ_BB_REG_REPORT) { bb_evt = (void *)buf; for (i=0; isize; i++) { printk("0x%08x: 0x%08x\n", bb_evt->base_addr + i*4, bb_evt->result[i]); } } else if (subevt == BT_HCI_EVT_VS_READ_RF_REG_REPORT) { rf_evt = (void *)buf; for (i=0; isize; i++) { printk("0x%04x: 0x%04x\n", rf_evt->base_addr + i, rf_evt->result[i]); } } else { printk("[BQB] unknown vendor event\n"); } } static void bqb_evt_handle(uint8_t *buf) { uint8_t evt = buf[0]; uint8_t len = buf[1]; if (len <= 0) { printk("[BQB] buf empty\n"); } if (evt == 0xff) { //HCI Vendor Event bqb_vs_evt_handle(buf + HCI_EVT_HDR_SIZE); } } int bt_bqb_vs_write_bb_reg(uint32_t addr, uint32_t val) { if (!bqb_info) { return -ENOMEM; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_WRITE_BB_REG; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_WRITE_BB_REG >> 8); bqb_info->tx_data[2] = 0x08; sys_put_le32(addr, &bqb_info->tx_data[3]); sys_put_le32(val, &bqb_info->tx_data[7]); return btdrv_send(HCI_CMD, bqb_info->tx_data, 11); } int bt_bqb_vs_read_bb_reg(uint32_t addr, uint8_t size) { if (!bqb_info) { return -ENOMEM; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_READ_BB_REG; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_READ_BB_REG >> 8); bqb_info->tx_data[2] = 0x05; sys_put_le32(addr, &bqb_info->tx_data[3]); bqb_info->tx_data[7] = size; return btdrv_send(HCI_CMD, bqb_info->tx_data, 8); } int bt_bqb_vs_write_rf_reg(uint16_t addr, uint16_t val) { if (!bqb_info) { return -ENOMEM; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_WRITE_RF_REG; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_WRITE_RF_REG >> 8); bqb_info->tx_data[2] = 0x04; sys_put_le16(addr, &bqb_info->tx_data[3]); sys_put_le16(val, &bqb_info->tx_data[5]); return btdrv_send(HCI_CMD, bqb_info->tx_data, 7); } int bt_bqb_vs_read_rf_reg(uint16_t addr, uint8_t size) { if (!bqb_info) { return -ENOMEM; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_READ_RF_REG; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_READ_RF_REG >> 8); bqb_info->tx_data[2] = 0x03; sys_put_le16(addr, &bqb_info->tx_data[3]); bqb_info->tx_data[5] = size; return btdrv_send(HCI_CMD, bqb_info->tx_data, 6); } #define BT_NAME_LEN (32+1) /* 32(len) + 1(NULL) */ static void bt_dut_mode_set_name(void) { #ifdef CONFIG_PROPERTY uint8_t *cfg_name = CFG_BT_NAME; int ret; ret = property_get(cfg_name, &bqb_info->tx_data[6], (BT_NAME_LEN-1)); if (ret < 0) { return; } bqb_info->tx_data[0] = 0x52; bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = (uint8_t)(ret + 3 + 2); bqb_info->tx_data[3] = 0x00; bqb_info->tx_data[4] = (uint8_t)(ret + 3); bqb_info->tx_data[5] = 0x09; bqb_info->tx_data[6 + ret] = 0x00; bqb_info->tx_data[6 + ret + 1] = 0x00; btdrv_send(HCI_CMD, bqb_info->tx_data, (uint16_t)(6 + ret + 2));/* HCI set name */ k_sleep(K_MSEC(20)); #endif } void bt_dut_mode(void) { if (!bqb_info) { return; } bqb_info->tx_data[0] = 0x03; bqb_info->tx_data[1] = 0x0c; /* ogf: baseband */ bqb_info->tx_data[2] = 0x00; /* len */ btdrv_send(HCI_CMD, bqb_info->tx_data, 3); /* HCI reset */ k_sleep(K_MSEC(50)); bt_dut_mode_set_name(); bqb_info->tx_data[0] = 0x1e; /* write inquiry scan activity */ bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x04; bqb_info->tx_data[3] = 0x00; bqb_info->tx_data[4] = 0x02; bqb_info->tx_data[5] = 0x60; bqb_info->tx_data[6] = 0x00; btdrv_send(HCI_CMD, bqb_info->tx_data, 7); k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x1c; /* write page scan activity */ bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x04; bqb_info->tx_data[3] = 0x00; bqb_info->tx_data[4] = 0x04; bqb_info->tx_data[5] = 0x80; bqb_info->tx_data[6] = 0x00; btdrv_send(HCI_CMD, bqb_info->tx_data, 7); k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x43; /* write inquiry scan type */ bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x01; bqb_info->tx_data[3] = 0x01; btdrv_send(HCI_CMD, bqb_info->tx_data, 4); k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x47; /* write page scan type */ bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x01; bqb_info->tx_data[3] = 0x01; btdrv_send(HCI_CMD, bqb_info->tx_data, 4); k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x1a; bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x01; bqb_info->tx_data[3] = 0x03; btdrv_send(HCI_CMD, bqb_info->tx_data, 4); /* HCI write scan */ k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x05; bqb_info->tx_data[1] = 0x0c; bqb_info->tx_data[2] = 0x03; bqb_info->tx_data[3] = 0x02; bqb_info->tx_data[4] = 0x00; bqb_info->tx_data[5] = 0x02; btdrv_send(HCI_CMD, bqb_info->tx_data, 6); /* HCI set event filter */ k_sleep(K_MSEC(20)); bqb_info->tx_data[0] = 0x03; bqb_info->tx_data[1] = 0x18; /* ogf: test */ bqb_info->tx_data[2] = 0x00; btdrv_send(HCI_CMD, bqb_info->tx_data, 3); /* HCI dut mode */ k_sleep(K_MSEC(20)); } static void bqb_uart_send(uint8_t *tx_data, uint16_t len) { int i; if (!bqb_info) { return; } for (i = 0; i < len; i++) { uart_poll_out(uart_dev, tx_data[i]); } } static int bqb_uart_tx_hci(uint8_t type, uint8_t *data) { uint16_t len; switch (type) { case HCI_ACL: len = (data[3]<<8 | data[2]) + HCI_ACL_HDR_SIZE; break; case HCI_SCO: len = data[2] + HCI_SCO_HDR_SIZE; break; case HCI_EVT: len = data[1] + HCI_EVT_HDR_SIZE; break; case HCI_ISO: len = (data[3]<<8 | data[2]) + HCI_ISO_HDR_SIZE; break; default: return -EINVAL; } uart_poll_out(uart_dev, type); bqb_uart_send(data, len); return 0; } static uint16_t bqb_uart_read(uint8_t *rx_data, uint16_t size) { uint16_t i; if (!bqb_info) { return 0; } for (i = 0; i < size; i++) { uart_poll_in(uart_dev, &rx_data[i]); } return i; } static void bqb_uart_rx_hci(uint8_t *buf) { uint8_t type = 0; bool has_hdr = false; uint16_t len, rd_len, total = 0; uart_poll_in(uart_dev, &type); //printk("read type: %d\n", type); switch (type) { case HCI_CMD: rd_len = bqb_uart_read(buf, HCI_CMD_HDR_SIZE); if (rd_len == HCI_CMD_HDR_SIZE) { has_hdr = true; len = buf[2]; } break; case HCI_ACL: rd_len = bqb_uart_read(buf, HCI_ACL_HDR_SIZE); if (rd_len == HCI_ACL_HDR_SIZE) { has_hdr = true; len = buf[3]<<8 | buf[2]; } break; case HCI_SCO: rd_len = bqb_uart_read(buf, HCI_SCO_HDR_SIZE); if (rd_len == HCI_SCO_HDR_SIZE) { has_hdr = true; len = buf[2]; } break; case HCI_ISO: rd_len = bqb_uart_read(buf, HCI_ISO_HDR_SIZE); if (rd_len == HCI_ISO_HDR_SIZE) { has_hdr = true; len = buf[3]<<8 | buf[2]; } break; default: printk("unknown type: %d\n", type); break; } //printk("read hdr: %d %d\n", rd_len, len); if (!has_hdr) { printk("uart rx incomplete\n"); return; } total += rd_len; rd_len = bqb_uart_read(buf+rd_len, len); if (rd_len != len) { printk("uart rx incomplete\n"); return; } total += rd_len; btdrv_send(type, buf, total); } static void bqb_uart_isr(const struct device *dev, void *user_data) { ARG_UNUSED(dev); ARG_UNUSED(user_data); if (uart_irq_rx_ready(uart_dev)) { if (bqb_info) { bqb_uart_rx_hci(bqb_info->tx_data); } } /* Clear the interrupt */ sys_write32(0x1, BQB_UART_STA); } #if BQB_USE_UART_0 #ifdef CONFIG_UART_CONSOLE static int _stdout_hook_null(int c) { (void)(c); return EOF; } #endif static int bqb_uart_init(void) { struct uart_config cfg; uart_dev = (struct device *)device_get_binding(BLE_BQB_UART); if (uart_dev == NULL) { printk("cannot get device %s\n", BLE_BQB_UART); return -ENODEV; } #ifdef CONFIG_UART_CONSOLE extern void __printk_hook_install(int (*fn)(int)); extern void __stdout_hook_install(int (*fn)(int)); __printk_hook_install(_stdout_hook_null); __stdout_hook_install(_stdout_hook_null); #endif uart_config_get(uart_dev, &cfg); cfg.baudrate = BQB_UART_SET_BR; uart_configure(uart_dev, &cfg); #ifdef CONFIG_ACTIONS_PRINTK_DMA uart_dma_send_stop(uart_dev); uart_fifo_switch(uart_dev, 1, UART_FIFO_TYPE_CPU); uart_dma_receive_stop(uart_dev); uart_fifo_switch(uart_dev, 0, UART_FIFO_TYPE_CPU); #endif uart_irq_rx_disable(uart_dev); uart_irq_callback_set(uart_dev, bqb_uart_isr); uart_irq_rx_enable(uart_dev); return 0; } #else #ifdef CONFIG_BOARD_LARK_DVB_EARPHONE #define BT_UART_MFP_SEL 6 static const struct acts_pin_config bqb_pin_cfg[] = { {12, BT_UART_MFP_SEL | GPIO_CTL_PADDRV_LEVEL(1) | GPIO_CTL_PULLUP}, {13, BT_UART_MFP_SEL | GPIO_CTL_PADDRV_LEVEL(1) | GPIO_CTL_PULLUP}, }; #else #define BT_UART_MFP_SEL 6 static const struct acts_pin_config bqb_pin_cfg[] = { {16, BT_UART_MFP_SEL | GPIO_CTL_PADDRV_LEVEL(1) | GPIO_CTL_PULLUP}, {17, BT_UART_MFP_SEL | GPIO_CTL_PADDRV_LEVEL(1) | GPIO_CTL_PULLUP}, }; #endif static int bqb_uart_init(void) { acts_pinmux_setup_pins(bqb_pin_cfg, ARRAY_SIZE(bqb_pin_cfg)); uart_dev = (struct device *)device_get_binding(BLE_BQB_UART); if (uart_dev == NULL) { printk("cannot get device %s\n", BLE_BQB_UART); return -ENODEV; } uart_irq_callback_set(uart_dev, bqb_uart_isr); uart_irq_rx_enable(uart_dev); return 0; } #endif static void *bqb_init_info(void) { uint32_t id; struct acts_bqb_data *bqb_info; id = ipmsg_create(RBUF_RAW, sizeof(struct acts_bqb_data)); if (id == 0) { return NULL; } rbuf_t *rbuf = RBUF_FR_OF(id); bqb_info = (void *)RBUF_FR_OF(rbuf->buf_off); memset(bqb_info, 0, sizeof(struct acts_bqb_data)); return bqb_info; } static uint8_t *bqb_get_buf(uint8_t type, uint8_t evt, uint16_t exp_len) { if (bqb_info) { bqb_info->rx_type = type; return bqb_info->rx_data; } else { return NULL; } } static int bqb_recv(uint16_t len) { if (bqb_info) { if (bqb_info->bqb_mode != 0) { bqb_uart_tx_hci(bqb_info->rx_type, bqb_info->rx_data); } if (bqb_info->rx_type == HCI_EVT) { bqb_evt_handle(bqb_info->rx_data); } } return 0; } #ifdef CONFIG_BT_CTRL_DEBUG #define BT_ANT_SW_SEL 23 static const struct acts_pin_config pin_cfg_le_bqb_dbg[] = { {56, BT_ANT_SW_SEL | GPIO_CTL_GPIO_OUTEN}, {57, BT_ANT_SW_SEL | GPIO_CTL_GPIO_OUTEN}, }; #endif static const btdrv_hci_cb_t cb = { .get_buf = bqb_get_buf, .recv = bqb_recv, }; /* BQB mode [0:BR BQB Test, 1:BLE BQB Test, 2:BR/BLE dual BQB Test] */ int bqb_init(int bqb_mode) { int err, need_uart; if (bqb_mode > 2) { printk("Error bqb_mode %d\n", bqb_mode); return -EINVAL; } bqb_info = bqb_init_info(); if (!bqb_info) { printk("Error bqb_init_info failed!\n"); return -ENOMEM; } bqb_info->bqb_in_test = 1; bqb_info->bqb_mode = (uint8_t)bqb_mode; need_uart = (bqb_info->bqb_mode == 0) ? 0 : 1; err = btdrv_init((btdrv_hci_cb_t *)&cb); if (err) { return -EINVAL; } if (need_uart) { err = bqb_uart_init(); if (err) { printk("bqb uart init failed %d\n", err); return -EINVAL; } } #ifdef CONFIG_BT_CTRL_DEBUG if (bqb_info->bqb_mode != 0) { acts_pinmux_setup_pins(pin_cfg_le_bqb_dbg, ARRAY_SIZE(pin_cfg_le_bqb_dbg)); } #endif if (bqb_info->bqb_mode != 1) { bt_dut_mode(); } printk("BQB mode %d [0:BR BQB Test, 1:BLE BQB Test, 2:BR/BLE dual BQB Test]\n", bqb_mode); return 0; } bool bt_bqb_is_in_test(void) { if (bqb_info) { return bqb_info->bqb_in_test ? true : false; } else { return false; } } int bt_bqb_vs_set_apll_temp_comp(uint8_t enable) { if(!bt_bqb_is_in_test()){ return -EIO; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_SET_APLL_TEMP_COMP; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_SET_APLL_TEMP_COMP >> 8); bqb_info->tx_data[2] = 0x01; bqb_info->tx_data[3] = enable; return btdrv_send(HCI_CMD, bqb_info->tx_data, 4); } int bt_bqb_vs_do_apll_temp_comp(void) { if(!bt_bqb_is_in_test()){ return -EIO; } bqb_info->tx_data[0] = (uint8_t)BT_HCI_OP_VS_DO_APLL_TEMP_COMP; bqb_info->tx_data[1] = (uint8_t)(BT_HCI_OP_VS_DO_APLL_TEMP_COMP >> 8); bqb_info->tx_data[2] = 0x0; return btdrv_send(HCI_CMD, bqb_info->tx_data, 3); }