/** Closes the interface */ static void cleanup( void ) { BTHCDBG("cleanup"); if (lib_running) { lib_running = 0; bthc_signal_event(HC_EVENT_EXIT); pthread_join(hc_cb.worker_thread, NULL); } userial_close(); p_hci_if->cleanup(); utils_cleanup(); /* Calling vendor-specific part */ if (bt_vnd_if) bt_vnd_if->cleanup(); pthread_cond_destroy(&hc_cb.cond); pthread_mutex_destroy(&hc_cb.mutex); bt_hc_cbacks = NULL; }
// Closes the interface. // This routine is not thread safe. static void cleanup(void) { if (has_cleaned_up) { ALOGW("%s Already cleaned up for this session\n", __func__); return; } BTHCDBG("cleanup"); if (hc_cb.worker_thread) { if (fwcfg_acked) { epilog_wait_timer(); // Stop reading thread userial_close_reader(); thread_post(hc_cb.worker_thread, event_epilog, NULL); } thread_free(hc_cb.worker_thread); pthread_mutex_lock(&hc_cb.worker_thread_lock); hc_cb.worker_thread = NULL; pthread_mutex_unlock(&hc_cb.worker_thread_lock); if (hc_cb.epilog_timer_created) { timer_delete(hc_cb.epilog_timer_id); hc_cb.epilog_timer_created = false; } } BTHCDBG("%s Finalizing cleanup\n", __func__); lpm_cleanup(); userial_close(); p_hci_if->cleanup(); utils_cleanup(); set_power(BT_VND_PWR_OFF); vendor_close(); pthread_mutex_destroy(&hc_cb.worker_thread_lock); fwcfg_acked = false; bt_hc_cbacks = NULL; has_cleaned_up = true; }
/** sends command HC controller to configure platform specific behaviour */ static int tx_hc_cmd(TRANSAC transac, char *p_buf, int len) { BTHCDBG("tx_hc_cmd: transac %p", transac); if (!transac) return BT_HC_STATUS_FAIL; thread_post(hc_cb.worker_thread, event_tx_cmd, transac); return BT_HC_STATUS_SUCCESS; }
/** Controls HCI logging on/off */ static int logging(bt_hc_logging_state_t state, char *p_path, bool save_existing) { BTHCDBG("logging %d", state); if (state != BT_HC_LOGGING_ON) btsnoop_close(); else if (p_path != NULL) btsnoop_open(p_path, save_existing); return BT_HC_STATUS_SUCCESS; }
/** Controls receive flow */ static int set_rxflow(bt_rx_flow_state_t state) { BTHCDBG("set_rxflow %d", state); p_userial_if->ioctl(\ ((state == BT_RXFLOW_ON) ? USERIAL_OP_RXFLOW_ON : USERIAL_OP_RXFLOW_OFF), \ NULL); return BT_HC_STATUS_SUCCESS; }
/** Chip power control */ static void set_power(bt_hc_chip_power_state_t state) { int pwr_state; BTHCDBG("set_power %d", state); /* Calling vendor-specific part */ pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF; vendor_send_command(BT_VND_OP_POWER_CTRL, &pwr_state); }
/** Chip power control */ static void set_power(bt_hc_chip_power_state_t state) { int pwr_state; BTHCDBG("set_power %d", state); /* Calling vendor-specific part */ pwr_state = (state == BT_HC_CHIP_PWR_ON) ? BT_VND_PWR_ON : BT_VND_PWR_OFF; if (bt_vnd_if) bt_vnd_if->op(BT_VND_OP_POWER_CTRL, &pwr_state); else ALOGE("vendor lib is missing!"); }
static void event_tx_cmd(void *msg) { HC_BT_HDR *p_msg = (HC_BT_HDR *)msg; BTHCDBG("%s: p_msg: %p, event: 0x%x", __func__, p_msg, p_msg->event); int event = p_msg->event & MSG_EVT_MASK; int sub_event = p_msg->event & MSG_SUB_EVT_MASK; if (event == MSG_CTRL_TO_HC_CMD && sub_event == BT_HC_AUDIO_STATE) { vendor_send_command(BT_VND_OP_SET_AUDIO_STATE, p_msg->data); } else { ALOGW("%s (event: 0x%x, sub_event: 0x%x) not supported", __func__, event, sub_event); } bt_hc_cbacks->dealloc(msg); }
/** Controls HCI logging on/off */ static int logging(bt_hc_logging_state_t state, char *p_path) { BTHCDBG("logging %d", state); if (state == BT_HC_LOGGING_ON) { if (p_path != NULL) btsnoop_open(p_path); } else { btsnoop_close(); } return BT_HC_STATUS_SUCCESS; }
static void event_tx(UNUSED_ATTR void *context) { /* * We will go through every packets in the tx queue. * Fine to clear tx_cmd_pkts_pending. */ tx_cmd_pkts_pending = false; HC_BT_HDR *sending_msg_que[64]; size_t sending_msg_count = 0; int sending_hci_cmd_pkts_count = 0; utils_lock(); HC_BT_HDR *p_next_msg = tx_q.p_first; while (p_next_msg && sending_msg_count < ARRAY_SIZE(sending_msg_que)) { if ((p_next_msg->event & MSG_EVT_MASK)==MSG_STACK_TO_HC_HCI_CMD) { /* * if we have used up controller's outstanding HCI command * credits (normally is 1), skip all HCI command packets in * the queue. * The pending command packets will be sent once controller * gives back us credits through CommandCompleteEvent or * CommandStatusEvent. */ if (tx_cmd_pkts_pending || (sending_hci_cmd_pkts_count >= num_hci_cmd_pkts)) { tx_cmd_pkts_pending = true; p_next_msg = utils_getnext(p_next_msg); continue; } sending_hci_cmd_pkts_count++; } HC_BT_HDR *p_msg = p_next_msg; p_next_msg = utils_getnext(p_msg); utils_remove_from_queue_unlocked(&tx_q, p_msg); sending_msg_que[sending_msg_count++] = p_msg; } utils_unlock(); for(size_t i = 0; i < sending_msg_count; i++) p_hci_if->send(sending_msg_que[i]); if (tx_cmd_pkts_pending) BTHCDBG("Used up Tx Cmd credits"); }
/** Closes the interface */ static void cleanup( void ) { BTHCDBG("cleanup"); if (lib_running) { if (fwcfg_acked == TRUE) { epilog_wait_timer(); bthc_signal_event(HC_EVENT_EPILOG); } else { bthc_signal_event(HC_EVENT_EXIT); } pthread_join(hc_cb.worker_thread, NULL); if (hc_cb.epilog_timer_created == 1) { timer_delete(hc_cb.epilog_timer_id); hc_cb.epilog_timer_created = 0; } } lib_running = 0; lpm_cleanup(); p_userial_if->close(); p_hci_if->cleanup(); utils_cleanup(); /* Calling vendor-specific part */ if (bt_vnd_if) bt_vnd_if->cleanup(); fwcfg_acked = FALSE; bt_hc_cbacks = NULL; }
/** Called post stack initialization */ static void postload(UNUSED_ATTR TRANSAC transac) { BTHCDBG("postload"); thread_post(hc_cb.worker_thread, event_postload, NULL); }
/******************************************************************************* ** ** Function bt_hc_worker_thread ** ** Description Mian worker thread ** ** Returns void * ** *******************************************************************************/ static void *bt_hc_worker_thread(void *arg) { uint16_t events; HC_BT_HDR *p_msg, *p_next_msg; ALOGI("bt_hc_worker_thread started"); prctl(PR_SET_NAME, (unsigned long)"bt_hc_worker", 0, 0, 0); tx_cmd_pkts_pending = FALSE; raise_priority_a2dp(TASK_HIGH_HCI_WORKER); while (lib_running) { pthread_mutex_lock(&hc_cb.mutex); while (ready_events == 0) { pthread_cond_wait(&hc_cb.cond, &hc_cb.mutex); } events = ready_events; ready_events = 0; pthread_mutex_unlock(&hc_cb.mutex); if (events & HC_EVENT_RX && ( p_hci_if->rcv != NULL)) { p_hci_if->rcv(); if ((tx_cmd_pkts_pending == TRUE) && (num_hci_cmd_pkts > 0)) { /* Got HCI Cmd Credits from Controller. * Prepare to send prior pending Cmd packets in the * following HC_EVENT_TX session. */ events |= HC_EVENT_TX; } } if (events & HC_EVENT_PRELOAD) { p_userial_if->open(USERIAL_PORT_1); /* Calling vendor-specific part */ if (bt_vnd_if) { bt_vnd_if->op(BT_VND_OP_FW_CFG, NULL); } else { if (bt_hc_cbacks) bt_hc_cbacks->preload_cb(NULL, BT_HC_PRELOAD_FAIL); } } if (events & HC_EVENT_POSTLOAD) { /* Start from SCO related H/W configuration, if SCO configuration * is required. Then, follow with reading requests of getting * ACL data length for both BR/EDR and LE. */ int result = -1; /* Calling vendor-specific part */ if (bt_vnd_if) result = bt_vnd_if->op(BT_VND_OP_SCO_CFG, NULL); if (result == -1) p_hci_if->get_acl_max_len(); } if (events & HC_EVENT_TX) { /* * We will go through every packets in the tx queue. * Fine to clear tx_cmd_pkts_pending. */ tx_cmd_pkts_pending = FALSE; HC_BT_HDR * sending_msg_que[64]; int sending_msg_count = 0; int sending_hci_cmd_pkts_count = 0; utils_lock(); p_next_msg = tx_q.p_first; while (p_next_msg && sending_msg_count < (int)sizeof(sending_msg_que)/sizeof(sending_msg_que[0])) { if ((p_next_msg->event & MSG_EVT_MASK)==MSG_STACK_TO_HC_HCI_CMD) { /* * if we have used up controller's outstanding HCI command * credits (normally is 1), skip all HCI command packets in * the queue. * The pending command packets will be sent once controller * gives back us credits through CommandCompleteEvent or * CommandStatusEvent. */ if ((tx_cmd_pkts_pending == TRUE) || (sending_hci_cmd_pkts_count >= num_hci_cmd_pkts)) { tx_cmd_pkts_pending = TRUE; p_next_msg = utils_getnext(p_next_msg); continue; } sending_hci_cmd_pkts_count++; } p_msg = p_next_msg; p_next_msg = utils_getnext(p_msg); utils_remove_from_queue_unlocked(&tx_q, p_msg); sending_msg_que[sending_msg_count++] = p_msg; } utils_unlock(); int i; for(i = 0; i < sending_msg_count; i++) p_hci_if->send(sending_msg_que[i]); if (tx_cmd_pkts_pending == TRUE) BTHCDBG("Used up Tx Cmd credits"); } if (events & HC_EVENT_LPM_ENABLE) { lpm_enable(TRUE); } if (events & HC_EVENT_LPM_DISABLE) { lpm_enable(FALSE); } if (events & HC_EVENT_LPM_IDLE_TIMEOUT) { lpm_wake_deassert(); } if (events & HC_EVENT_LPM_ALLOW_SLEEP) { lpm_allow_bt_device_sleep(); } if (events & HC_EVENT_LPM_WAKE_DEVICE) { lpm_wake_assert(); } if (events & HC_EVENT_EPILOG) { /* Calling vendor-specific part */ if (bt_vnd_if) bt_vnd_if->op(BT_VND_OP_EPILOG, NULL); else break; // equivalent to HC_EVENT_EXIT } if (events & HC_EVENT_EXIT) break; } ALOGI("bt_hc_worker_thread exiting"); lib_running = 0; pthread_exit(NULL); return NULL; // compiler friendly }
/** Called post stack initialization */ static void postload(TRANSAC transac) { BTHCDBG("postload"); bthc_signal_event(HC_EVENT_POSTLOAD); }
/** Called prio to stack initialization */ static void preload(TRANSAC transac) { BTHCDBG("preload"); bthc_signal_event(HC_EVENT_PRELOAD); }
static void ssr_cleanup (void) { BTHCDBG("ssr_cleanup"); /* Calling vendor-specific part */ if (bt_vnd_if) bt_vnd_if->ssr_cleanup(); }