#include #include #include #include "fr30xx.h" #include "btdm_host.h" #include "FreeRTOS.h" #include "timers.h" #include "semphr.h" #include "fdb_app.h" #include "host.h" #include "me_api.h" #include "bt_types.h" #include "app_config.h" #define HOST_DDB_RECORD_COUNT 8 #define HOST_DDB_INFOR_VERSION 0x01 #define HCI_UART UART0 #define HCI_UART_IRQn UART0_IRQn /* Uart backup/restore */ typedef struct { volatile uint8_t BAK_DLL; volatile uint8_t BAK_DLH; volatile uint8_t BAK_DLF; volatile uint8_t BAK_IER; volatile uint8_t BAK_FCR; volatile uint8_t BAK_LCR; volatile uint8_t BAK_MCR; }struct_UartRES_t; struct host_ddb_info { uint8_t version; uint8_t used_count; BtDeviceRecord ddb[HOST_DDB_RECORD_COUNT]; }; static TimerHandle_t btdm_host_timer = NULL; static bool btdm_host_timer_inited = false; static const uint8_t *write_buffer = 0; static uint32_t write_length = 0; static void (*write_callback)(void) = 0; static uint8_t *read_buffer = 0; static uint32_t read_length = 0; static void (*read_callback)(void *, uint8_t) = 0; static void *read_dummy = 0; /* host semaphore handle */ SemaphoreHandle_t host_Semaphore_handle; /* host task handle */ static TaskHandle_t btdm_host_handle; /* hardware handlers */ static UART_HandleTypeDef HCI_handle; /* structure used to save and restore uart configuration when sleep mode is enabled */ static struct_UartRES_t uart_regs; #define READ_REG(__ADDR__) (*(volatile uint32_t *)(__ADDR__)) #define WRTIE_REG(__ADDR__, __VALUE__) (*(volatile uint32_t *)(__ADDR__) = (__VALUE__)) /************************************************************************************ * @fn Uart_backup/Uart_restore * * @brief Uart low-power backup/restore */ __RAM_CODE static void __Uart_backup(struct_UART_t *Uartx, struct_UartRES_t *UartRES) { volatile uint32_t UartxBase = (uint32_t)Uartx; UartRES->BAK_LCR = READ_REG(UartxBase + 0x0C); UartRES->BAK_MCR = READ_REG(UartxBase + 0x10); UartRES->BAK_FCR = 0x09; UartRES->BAK_IER = READ_REG(UartxBase + 0x04); WRTIE_REG(UartxBase + 0x0C, UartRES->BAK_LCR | 0x80); UartRES->BAK_DLL = READ_REG(UartxBase); UartRES->BAK_DLH = READ_REG(UartxBase + 0x04); UartRES->BAK_DLF = READ_REG(UartxBase + 0xC0); WRTIE_REG(UartxBase + 0x0C, UartRES->BAK_LCR); } __RAM_CODE static void __Uart_restore(struct_UART_t *Uartx, struct_UartRES_t *UartRES) { volatile uint32_t UartxBase = (uint32_t)Uartx; WRTIE_REG(UartxBase + 0x04, UartRES->BAK_IER); WRTIE_REG(UartxBase + 0x08, UartRES->BAK_FCR); WRTIE_REG(UartxBase + 0x10, UartRES->BAK_MCR); WRTIE_REG(UartxBase + 0x0C, UartRES->BAK_LCR | 0x80); WRTIE_REG(UartxBase, UartRES->BAK_DLL); WRTIE_REG(UartxBase + 0x04, UartRES->BAK_DLH); WRTIE_REG(UartxBase + 0xC0, UartRES->BAK_DLF); WRTIE_REG(UartxBase + 0x0C, UartRES->BAK_LCR); } static void hci_uart_rx_callback(UART_HandleTypeDef *h) { read_length = 0; read_callback(read_dummy, 0); } static void hci_uart_tx_callback(UART_HandleTypeDef *h) { write_length = 0; write_callback(); system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_TYPE_HCI_TX); } static void vTimerCallback( TimerHandle_t pxTimer ) { btdm_timer_trigger(); } void btdm_timer_start(uint32_t ms) { if (!btdm_host_timer_inited) { btdm_host_timer = xTimerCreate( "Timer", ms / portTICK_PERIOD_MS, pdTRUE, NULL, vTimerCallback); btdm_host_timer_inited = true; } xTimerStop(btdm_host_timer, portMAX_DELAY); xTimerChangePeriod(btdm_host_timer, ms / portTICK_RATE_MS, portMAX_DELAY); xTimerStart(btdm_host_timer, portMAX_DELAY); } void btdm_timer_stop(void) { if(btdm_host_timer) { xTimerStop(btdm_host_timer, portMAX_DELAY); } } uint32_t btdm_get_system_time(void) { return xTaskGetTickCount() * portTICK_RATE_MS; } int btdm_time_diff(uint32_t a_time, uint32_t b_time) { return b_time - a_time; } void hci_if_do_send(const uint8_t *buffer, uint32_t length, void (*func)(void)) { struct app_task_event * event; uint32_t print_size = length > 8 ? 8 : length; assert(write_length == 0); write_buffer = buffer; write_length = length; write_callback = func; #if 1 system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_TYPE_HCI_TX); uart_transmit_IT(&HCI_handle, (void *)buffer, length); #else uart_transmit(&HCI_handle, (void *)buffer, length); write_callback(); write_length = 0; #endif } void hci_if_do_recv(uint8_t *bufptr, uint32_t size, void *arg, void* dummy) { struct app_task_event * event; assert(read_length == 0); read_buffer = bufptr; read_length = size; read_callback = (void (*)(void *, uint8_t))arg; read_dummy = dummy; uart_receive_IT(&HCI_handle, bufptr, size); } void btdm_host_notify_schedule(void) { if(xPortIsInsideInterrupt()) { vTaskNotifyGiveFromISR(btdm_host_handle, NULL); } else { xTaskNotifyGive(btdm_host_handle); } } __WEAK void host_ready_cb(void) { // struct app_task_event *event; // /* notify application BTDM stack is ready. */ // event = app_task_event_alloc(APP_TASK_EVENT_HOST_INITED, 0, true); // app_task_event_post(event, false); } static void host_hw_init(uint32_t baudrate) { GPIO_InitTypeDef gpio_config; /* configure PA0 and PA1 to UART0 function */ gpio_config.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; gpio_config.Mode = GPIO_MODE_AF_PP; gpio_config.Pull = GPIO_PULLUP; gpio_config.Alternate = GPIO_FUNCTION_1; gpio_init(GPIOA, &gpio_config); /* UART0: used for Log and AT command */ HCI_handle.UARTx = HCI_UART; HCI_handle.Init.BaudRate = baudrate; HCI_handle.Init.DataLength = UART_DATA_LENGTH_8BIT; HCI_handle.Init.StopBits = UART_STOPBITS_1; HCI_handle.Init.Parity = UART_PARITY_NONE; HCI_handle.Init.FIFO_Mode = UART_FIFO_ENABLE; HCI_handle.TxCpltCallback = hci_uart_tx_callback; HCI_handle.RxCpltCallback = hci_uart_rx_callback; uart_init(&HCI_handle); /* keep RTS is inactive before HCI is ready */ __UART_AUTO_FLOW_CONTROL_DISABLE(HCI_handle.UARTx); __UART_RTS_INACTIVE(HCI_handle.UARTx); // __UART_RxFIFO_THRESHOLD((&HCI_handle), 2); NVIC_SetPriority(HCI_UART_IRQn, 2); NVIC_EnableIRQ(HCI_UART_IRQn); } static void host_btdm_task(void *ble_static_addr) { struct host_ddb_info *info; size_t size; /* Initialize BLE stack */ struct ble_host_param param; if (ble_static_addr) { param.own_addr_type = 1; memcpy(¶m.own_addr.addr[0], ble_static_addr, 6); } else { param.own_addr_type = 0; } host_Semaphore_handle = xSemaphoreCreateRecursiveMutex(); ble_host_init(¶m); /* HCI is ready */ __UART_AUTO_FLOW_CONTROL_ENABLE(HCI_handle.UARTx); __UART_RTS_ACTIVE(HCI_handle.UARTx); while(ble_host_ready() == false) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); btdm_host_schedule_ble(); } /* check whether link key information stored in flashdb is valid or not */ info = pvPortMalloc(sizeof(struct host_ddb_info)); size = flashdb_get(FDB_KEY_BT_LINKKEY, (void *)info, sizeof(struct host_ddb_info)); if ((size != 0) && ((sizeof(struct host_ddb_info) != size) || (info->version != HOST_DDB_INFOR_VERSION) || (info->used_count > HOST_DDB_RECORD_COUNT))) { flashdb_del(FDB_KEY_BT_LINKKEY); } vPortFree(info); // Initialize BT stack bt_host_init(); #if BTDM_STACK_ENABLE_A2DP_SNK | BTDM_STACK_ENABLE_A2DP_SRC bt_a2dp_init(); #endif #if BTDM_STACK_ENABLE_AVRCP bt_avrcp_init(); #endif #if BTDM_STACK_ENABLE_HF bt_hf_init(); #endif #if BTDM_STACK_ENABLE_AG bt_hfg_init(); #endif #if BTDM_STACK_ENABLE_PAN bt_pan_init(); #endif #if BTDM_STACK_ENABLE_PBAP bt_pbap_init(); #endif #if BTDM_STACK_ENABLE_SPP bt_spp_init(); #endif while(bt_host_ready() == false) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); btdm_host_schedule(); } host_ready_cb(); while(1) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); btdm_host_schedule(); } } static void host_ble_task(void *ble_static_addr) { struct app_task_event *event; struct host_ddb_info *info; size_t size; /* Initialize BLE stack */ struct ble_host_param param; if (ble_static_addr) { param.own_addr_type = 1; memcpy(¶m.own_addr.addr[0], ble_static_addr, 6); } else { param.own_addr_type = 0; } ble_host_init(¶m); printf("HCI is ready.\r\n"); /* HCI is ready */ __UART_AUTO_FLOW_CONTROL_ENABLE(HCI_handle.UARTx); __UART_RTS_ACTIVE(HCI_handle.UARTx); while(ble_host_ready() == false) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); btdm_host_schedule_ble(); } host_ready_cb(); while(1) { ulTaskNotifyTake(pdFALSE, portMAX_DELAY); btdm_host_schedule_ble(); } } /************************************************************************************ * @fn host_btdm_start * * @brief Initializes bt dual mode host, host task will be created in this function. * host_ble_start and host_btdm_start should not be called together. * * @param baudrate: uart baudrate of HCI * stack_size: stack size of host task * priority: priority of host task * ble_static_addr: If public address will be used as identity address, this field * should be set NULL. Otherwise random static address stored in * ble_static_addr will be used as identity address. */ void host_btdm_start(uint32_t baudrate, uint32_t stack_size, uint8_t priority, const uint8_t *ble_static_addr) { host_hw_init(baudrate); xTaskCreate(host_btdm_task, "host", stack_size, (void *)ble_static_addr, priority, &btdm_host_handle); } /************************************************************************************ * @fn host_ble_start * * @brief Initializes ble host, host task will be created in this function. * host_ble_start and host_btdm_start should not be called together. * * @param baudrate: uart baudrate of HCI * stack_size: stack size of host task * priority: priority of host task * ble_static_addr: If public address will be used as identity address, this field * should be set NULL. Otherwise random static address stored in * ble_static_addr will be used as identity address. */ void host_ble_start(uint32_t baudrate, uint32_t stack_size, uint8_t priority, const uint8_t *ble_static_addr) { host_hw_init(baudrate); xTaskCreate(host_ble_task, "host", stack_size, (void *)ble_static_addr, priority, &btdm_host_handle); } //void host_stop(void) //{ // uint32_t *dst, *src, *end; // if(btdm_host_timer_inited) { // xTimerDelete(btdm_host_timer, portMAX_DELAY); // btdm_host_timer = NULL; // btdm_host_timer_inited = false; // } // vTaskDelete(btdm_host_handle); // // btdm_mem_uninit(); // write_buffer = 0; // write_length = 0; // write_callback = 0; // read_buffer = 0; // read_length = 0; // read_callback = 0; // read_dummy = 0; // // dst = (uint32_t *)&Image$$HOST_DATA$$RW$$Base; // src=(uint32_t *)&Load$$HOST_DATA$$RW$$Base; // end = (uint32_t *)&Load$$HOST_DATA$$RW$$Limit; // for(; (uint32_t)src<(uint32_t)end;) // { // *dst++ = *src++; // } // dst = (uint32_t *)&Image$$HOST_DATA$$ZI$$Base; // end = (uint32_t *)&Image$$HOST_DATA$$ZI$$Limit; // for(; dst < end;) // { // *dst++ = 0; // } //} BtStatus DDB_AddRecord(const BtDeviceRecord* record) { fdb_err_t err; struct host_ddb_info *info; size_t size; info = pvPortMalloc(sizeof(struct host_ddb_info)); if (info) { uint32_t index = 0; size = flashdb_get(FDB_KEY_BT_LINKKEY, (void *)info, sizeof(struct host_ddb_info)); if (size == 0) { info->version = HOST_DDB_INFOR_VERSION; info->used_count = 0; } else { if ((size != sizeof(struct host_ddb_info)) || (info->version != HOST_DDB_INFOR_VERSION) || (info->used_count > HOST_DDB_RECORD_COUNT)) { if (flashdb_del(FDB_KEY_BT_LINKKEY) != FDB_NO_ERR) { vPortFree(info); return BT_STATUS_FAILED; } else { info->version = HOST_DDB_INFOR_VERSION; info->used_count = 0; } } } /* search for duplicated record according to bluetooth device address */ for (index = 0; index < info->used_count; index++) { if (memcmp((void *)&info->ddb[index].bdAddr.A, (void *)&record->bdAddr, sizeof(BD_ADDR)) == 0) { break; } } if (index < info->used_count) { /* duplicated device is found, delete the found device and move forward the information of subsequence devices */ memcpy((void *)&info->ddb[index], (void *)&info->ddb[index+1], sizeof(BtDeviceRecord) * (info->used_count-1-index)); index = info->used_count - 1; } else { if (info->used_count == HOST_DDB_RECORD_COUNT) { /* the table is full, remove the oldest device information */ memcpy((void *)&info->ddb[0], (void *)&info->ddb[1], sizeof(BtDeviceRecord) * (HOST_DDB_RECORD_COUNT-1)); index = HOST_DDB_RECORD_COUNT-1; } else { index = info->used_count; info->used_count++; } } memcpy((void *)&info->ddb[index], (void *)record, sizeof(BtDeviceRecord)); printf("linkey: "); for(uint8_t i=0; i<16; i++){ printf("%02x ",record->linkKey[i]); } printf("\r\n"); err = flashdb_set(FDB_KEY_BT_LINKKEY, (void *)info, sizeof(struct host_ddb_info)); vPortFree(info); if (err != FDB_NO_ERR) { return BT_STATUS_FAILED; } else { return BT_STATUS_SUCCESS; } } else { return BT_STATUS_FAILED; } } BtStatus DDB_FindRecord(const BD_ADDR *bdAddr, BtDeviceRecord* record) { fdb_err_t err; struct host_ddb_info *info; size_t size; BtStatus status; info = pvPortMalloc(sizeof(struct host_ddb_info)); if (info) { uint32_t index = 0; size = flashdb_get(FDB_KEY_BT_LINKKEY, (void *)info, sizeof(struct host_ddb_info)); if (size == 0) { vPortFree(info); return BT_STATUS_FAILED; } else { if ((size != sizeof(struct host_ddb_info)) || (info->version != HOST_DDB_INFOR_VERSION) || (info->used_count > HOST_DDB_RECORD_COUNT)) { flashdb_del(FDB_KEY_BT_LINKKEY); vPortFree(info); return BT_STATUS_FAILED; } } /* search for duplicated record according to bluetooth device address */ for (index = 0; index < info->used_count; index++) { if (memcmp((void *)&info->ddb[index].bdAddr.A, (void *)bdAddr, sizeof(BD_ADDR)) == 0) { break; } } if (index < info->used_count) { /* information is found */ memcpy((void *)record, (void *)&info->ddb[index], sizeof(BtDeviceRecord)); status = BT_STATUS_SUCCESS; if ((index+1) != info->used_count) { memcpy((void *)&info->ddb[index], (void *)&info->ddb[index+1], sizeof(BtDeviceRecord) * (info->used_count-1-index)); memcpy((void *)&info->ddb[info->used_count - 1], (void *)record, sizeof(BtDeviceRecord)); err = flashdb_set(FDB_KEY_BT_LINKKEY, (void *)info, sizeof(struct host_ddb_info)); if (err != FDB_NO_ERR) { status = BT_STATUS_FAILED; } } } else { status = BT_STATUS_FAILED; } vPortFree(info); return status; } else { return BT_STATUS_FAILED; } } enum btdm_nvds_status btdm_nvds_put(uint8_t tag, uint16_t length, uint8_t *data) { enum btdm_nvds_status status = BTDM_NVDS_STATUS_FAILED; uint32_t fdb_key; if (tag == BTDM_NVDS_TAG_CONTROLLER_INFO) { fdb_key = FDB_KEY_CONTROLLER_INFO; } else { fdb_key = FDB_KEY_BTDM_LIB_BASE | tag; } if (flashdb_set(fdb_key, data, length) == FDB_NO_ERR) { status = BTDM_NVDS_STATUS_OK; } else { status = BTDM_NVDS_STATUS_FAILED; } return status; } enum btdm_nvds_status btdm_nvds_get(uint8_t tag, uint16_t *length, uint8_t *buffer) { *length = flashdb_get(FDB_KEY_BTDM_LIB_BASE | tag, buffer, *length); if (*length == 0) { return BTDM_NVDS_STATUS_FAILED; } else { return BTDM_NVDS_STATUS_OK; } } enum btdm_nvds_status btdm_nvds_del(uint8_t tag) { enum btdm_nvds_status status = BTDM_NVDS_STATUS_FAILED; if (flashdb_del(FDB_KEY_BTDM_LIB_BASE | tag) == FDB_NO_ERR) { status = BTDM_NVDS_STATUS_OK; } else { status = BTDM_NVDS_STATUS_FAILED; } return status; } void uart0_irq(void) { uart_IRQHandler(&HCI_handle); } /************************************************************************************ * @fn host_before_sleep_check * * @brief user should call this function to check whether HCI is in busy state before * enter sleep mode. When HCI is in IDLE state, this function will return True and * RTS will be changed to GPIO mode and output high level to suspend HCI transfer. * * @return True: HCI is in IDLE state, user is allowed to enter sleep mode. * False: HCI is in busy state, user is not allowed to enter sleep mode. */ __RAM_CODE bool host_before_sleep_check(void) { if (__UART_IS_TxFIFO_EMPTY(HCI_handle.UARTx) == 0) { return false; } /* set RTS to notice host device that UART is not available*/ __SYSTEM_GPIOA_CLK_ENABLE(); GPIOA->GPIO_OutputEN &= (~GPIO_PIN_3); GPIOA->GPIO_BIT_SET = GPIO_PIN_3; SYSTEM->PortA_L_FuncMux &= (~(0xf<<12)); system_delay_us(100); if (__UART_IS_RxFIFO_EMPTY(HCI_handle.UARTx)) { /* keep RTS pin is controllered by GPIO */ __Uart_backup(HCI_handle.UARTx, &uart_regs); return true; } else { /* recover RTS pin is controllered by UART */ SYSTEM->PortA_L_FuncMux |= (GPIO_FUNCTION_1<<12); return false; } } /************************************************************************************ * @fn host_hci_reinit * * @brief When system wake up from sleep mode, user should call this function to recover * HCI. */ __RAM_CODE void host_hci_reinit(void) { GPIO_InitTypeDef gpio_config; /* configure PA0 and PA1 to UART0 function */ gpio_config.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3; gpio_config.Mode = GPIO_MODE_AF_PP; gpio_config.Pull = GPIO_PULLUP; gpio_config.Alternate = GPIO_FUNCTION_1; gpio_init(GPIOA, &gpio_config); __SYSTEM_UART0_CLK_ENABLE(); __Uart_restore(HCI_handle.UARTx, &uart_regs); NVIC_SetPriority(HCI_UART_IRQn, 2); NVIC_EnableIRQ(HCI_UART_IRQn); } void btdm_host_lock(void) { // volatile uint32_t address; // __asm("MOV %[result], LR":[result] "=r" (address)); if(xPortIsInsideInterrupt() == 0){ xSemaphoreTakeRecursive(host_Semaphore_handle,portMAX_DELAY); } else{ while(1); //xSemaphoreTakeFromISR(host_Semaphore_handle,NULL); } } void btdm_host_unlock(void) { if(xPortIsInsideInterrupt() == 0){ xSemaphoreGiveRecursive(host_Semaphore_handle); } else{ while(1); } }