Example #1
0
/*******************************************************************************
**
** Function         port_set_defaults
**
** Description      Set defualt port parameters
**
**
*******************************************************************************/
void port_set_defaults (tPORT *p_port)
{
    p_port->ev_mask        = 0;
    p_port->p_callback     = NULL;
    p_port->port_ctrl      = 0;
    p_port->error          = 0;
    p_port->line_status    = 0;
    p_port->rx_flag_ev_pending = FALSE;
    p_port->peer_mtu       = RFCOMM_DEFAULT_MTU;

    p_port->user_port_pars = default_port_pars;
    p_port->peer_port_pars = default_port_pars;

    p_port->credit_tx      = 0;
    p_port->credit_rx      = 0;
    /*  p_port->credit_rx_max  = PORT_CREDIT_RX_MAX;            Determined later */
    /*  p_port->credit_rx_low  = PORT_CREDIT_RX_LOW;            Determined later */

    memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl));
    memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl));
    memset (&p_port->rx, 0, sizeof (p_port->rx));
    memset (&p_port->tx, 0, sizeof (p_port->tx));

    p_port->tx.queue = fixed_queue_new(SIZE_MAX);
    p_port->rx.queue = fixed_queue_new(SIZE_MAX);
}
Example #2
0
/*******************************************************************************
**
** Function         btm_init
**
** Description      This function is called at BTM startup to allocate the
**                  control block (if using dynamic memory), and initializes the
**                  tracing level.  It then initializes the various components of
**                  btm.
**
** Returns          void
**
*******************************************************************************/
void btm_init (void)
{
#if BTM_DYNAMIC_MEMORY
    btm_cb_ptr = (tBTM_CB *)osi_malloc(sizeof(tBTM_CB));
#endif /* #if BTM_DYNAMIC_MEMORY */
    /* All fields are cleared; nonzero fields are reinitialized in appropriate function */
    memset(&btm_cb, 0, sizeof(tBTM_CB));
    btm_cb.page_queue = fixed_queue_new(QUEUE_SIZE_MAX);
    btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX);

#if defined(BTM_INITIAL_TRACE_LEVEL)
    btm_cb.trace_level = BTM_INITIAL_TRACE_LEVEL;
#else
    btm_cb.trace_level = BT_TRACE_LEVEL_NONE;
#endif
    /* Initialize BTM component structures */
    btm_inq_db_init();                  /* Inquiry Database and Structures */
    btm_acl_init();                     /* ACL Database and Structures */
#if (SMP_INCLUDED == TRUE)
    btm_sec_init(BTM_SEC_MODE_SP);      /* Security Manager Database and Structures */
#endif  ///SMP_INCLUDED == TRUE
#if BTM_SCO_INCLUDED == TRUE
    btm_sco_init();                     /* SCO Database and Structures (If included) */
#endif

    btm_dev_init();                     /* Device Manager Structures & HCI_Reset */
    btm_lock_init();
    btm_sem_init();
}
Example #3
0
eager_reader_t *eager_reader_new(
    int fd_to_read,
    const allocator_t *allocator,
    size_t buffer_size,
    size_t max_buffer_count,
    const char *thread_name) {

  assert(fd_to_read != INVALID_FD);
  assert(allocator != NULL);
  assert(buffer_size > 0);
  assert(max_buffer_count > 0);
  assert(thread_name != NULL && *thread_name != '\0');

  eager_reader_t *ret = osi_calloc(sizeof(eager_reader_t));
  if (!ret) {
    LOG_ERROR(LOG_TAG, "%s unable to allocate memory for new eager_reader.", __func__);
    goto error;
  }

  ret->allocator = allocator;
  ret->inbound_fd = fd_to_read;

  ret->bytes_available_fd = eventfd(0, 0);
  if (ret->bytes_available_fd == INVALID_FD) {
    LOG_ERROR(LOG_TAG, "%s unable to create output reading semaphore.", __func__);
    goto error;
  }

  ret->buffer_size = buffer_size;

  ret->buffers = fixed_queue_new(max_buffer_count);
  if (!ret->buffers) {
    LOG_ERROR(LOG_TAG, "%s unable to create buffers queue.", __func__);
    goto error;
  }

  ret->inbound_read_thread = thread_new(thread_name);
  if (!ret->inbound_read_thread) {
    LOG_ERROR(LOG_TAG, "%s unable to make reading thread.", __func__);
    goto error;
  }

  ret->inbound_read_object = reactor_register(
    thread_get_reactor(ret->inbound_read_thread),
    fd_to_read,
    ret,
    inbound_data_waiting,
    NULL
  );

  return ret;

error:;
  eager_reader_free(ret);
  return NULL;
}
Example #4
0
/*******************************************************************************
**
** Function         rfc_alloc_multiplexer_channel
**
** Description      This function returns existing or new control block for
**                  the BD_ADDR.
**
*******************************************************************************/
tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator)
{
    int i, j;
    tRFC_MCB *p_mcb = NULL;
    RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel: bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
                       bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
    RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d", is_initiator);

    for (i = 0; i < MAX_BD_CONNECTIONS; i++) {
        RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel rfc_cb.port.rfc_mcb[%d].state:%d",
                           i, rfc_cb.port.rfc_mcb[i].state);
        RFCOMM_TRACE_DEBUG("(rfc_cb.port.rfc_mcb[i].bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
                           rfc_cb.port.rfc_mcb[i].bd_addr[0], rfc_cb.port.rfc_mcb[i].bd_addr[1],
                           rfc_cb.port.rfc_mcb[i].bd_addr[2], rfc_cb.port.rfc_mcb[i].bd_addr[3],
                           rfc_cb.port.rfc_mcb[i].bd_addr[4], rfc_cb.port.rfc_mcb[i].bd_addr[5]);

        if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE)
                && (!memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN))) {
            /* Multiplexer channel found do not change anything */
            /* If there was an inactivity timer running stop it now */
            if (rfc_cb.port.rfc_mcb[i].state == RFC_MX_STATE_CONNECTED) {
                rfc_timer_stop (&rfc_cb.port.rfc_mcb[i]);
            }
            RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, found, state:%d, p_mcb:%p",
                               is_initiator, rfc_cb.port.rfc_mcb[i].state, &rfc_cb.port.rfc_mcb[i]);
            return (&rfc_cb.port.rfc_mcb[i]);
        }
    }

    /* connection with bd_addr does not exist */
    for (i = 0, j = rfc_cb.rfc.last_mux + 1; i < MAX_BD_CONNECTIONS; i++, j++) {
        if (j >= MAX_BD_CONNECTIONS) {
            j = 0;
        }

        p_mcb = &rfc_cb.port.rfc_mcb[j];
        if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) {
            /* New multiplexer control block */
            fixed_queue_free(p_mcb->cmd_q, NULL);
            memset (p_mcb, 0, sizeof (tRFC_MCB));
            memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN);
            RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, create new p_mcb:%p, index:%d",
                               is_initiator, &rfc_cb.port.rfc_mcb[j], j);

            p_mcb->cmd_q = fixed_queue_new(SIZE_MAX);

            p_mcb->is_initiator = is_initiator;

            rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER);

            rfc_cb.rfc.last_mux = (UINT8) j;
            return (p_mcb);
        }
    }
    return (NULL);
}
Example #5
0
static void btc_a2dp_sink_thread_init(UNUSED_ATTR void *context)
{
    APPL_TRACE_EVENT("%s\n", __func__);
    memset(&btc_aa_snk_cb, 0, sizeof(btc_aa_snk_cb));

    btc_a2dp_sink_state = BTC_A2DP_SINK_STATE_ON;

    btc_aa_snk_cb.RxSbcQ = fixed_queue_new(SIZE_MAX);

    btc_a2dp_control_init();
}
Example #6
0
static int hci_layer_init_env(void)
{
    command_waiting_response_t *cmd_wait_q;

    // The host is only allowed to send at most one command initially,
    // as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control)
    // This value can change when you get a command complete or command status event.
    hci_host_env.command_credits = 1;
    hci_host_env.command_queue = fixed_queue_new(SIZE_MAX);
    if (hci_host_env.command_queue) {
        fixed_queue_register_dequeue(hci_host_env.command_queue, event_command_ready);
    } else {
        LOG_ERROR("%s unable to create pending command queue.", __func__);
        return -1;
    }

    hci_host_env.packet_queue = fixed_queue_new(SIZE_MAX);
    if (hci_host_env.packet_queue) {
        fixed_queue_register_dequeue(hci_host_env.packet_queue, event_packet_ready);
    } else {
        LOG_ERROR("%s unable to create pending packet queue.", __func__);
        return -1;
    }

    // Init Commands waiting response list and timer
    cmd_wait_q = &hci_host_env.cmd_waiting_q;
    cmd_wait_q->timer_is_set = false;
    cmd_wait_q->commands_pending_response = list_new(NULL);
    if (!cmd_wait_q->commands_pending_response) {
        LOG_ERROR("%s unable to create list for commands pending response.", __func__);
        return -1;
    }
    pthread_mutex_init(&cmd_wait_q->commands_pending_response_lock, NULL);
    cmd_wait_q->command_response_timer = osi_alarm_new("cmd_rsp_to", command_timed_out, cmd_wait_q, COMMAND_PENDING_TIMEOUT);
    if (!cmd_wait_q->command_response_timer) {
        LOG_ERROR("%s unable to create command response timer.", __func__);
        return -1;
    }

    return 0;
}
Example #7
0
/*******************************************************************************
**
** Function         avdt_scb_alloc
**
** Description      Allocate a stream control block.
**
**
** Returns          pointer to the scb, or NULL if none could be allocated.
**
*******************************************************************************/
tAVDT_SCB *avdt_scb_alloc(tAVDT_CS *p_cs)
{
    tAVDT_SCB   *p_scb = &avdt_cb.scb[0];
    int         i;

    /* find available scb */
    for (i = 0; i < AVDT_NUM_SEPS; i++, p_scb++) {
        if (!p_scb->allocated) {
            memset(p_scb, 0, sizeof(tAVDT_SCB));
            p_scb->allocated = TRUE;
            p_scb->p_ccb = NULL;

            /* initialize sink as activated */
            if (p_cs->tsep == AVDT_TSEP_SNK) {
                p_scb->sink_activated = TRUE;
            }

            memcpy(&p_scb->cs, p_cs, sizeof(tAVDT_CS));
#if AVDT_MULTIPLEXING == TRUE
            /* initialize fragments gueue */
            p_scb->frag_q = fixed_queue_new(SIZE_MAX);

            if (p_cs->cfg.psc_mask & AVDT_PSC_MUX) {
                p_scb->cs.cfg.mux_tcid_media = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);
#if AVDT_REPORTING == TRUE
                if (p_cs->cfg.psc_mask & AVDT_PSC_REPORT) {
                    p_scb->cs.cfg.mux_tcid_report = avdt_ad_type_to_tcid(AVDT_CHAN_REPORT, p_scb);
                }
#endif
            }
#endif
            p_scb->timer_entry.param = (UINT32) p_scb;
            AVDT_TRACE_DEBUG("avdt_scb_alloc hdl=%d, psc_mask:0x%x\n", i + 1, p_cs->cfg.psc_mask);
            break;
        }
    }

    if (i == AVDT_NUM_SEPS) {
        /* out of ccbs */
        p_scb = NULL;
        AVDT_TRACE_WARNING("Out of scbs");
    }

    return p_scb;
}
Example #8
0
thread_t *thread_new_sized(const char *name, size_t work_queue_capacity) {
  assert(name != NULL);
  assert(work_queue_capacity != 0);

  thread_t *ret = osi_calloc(sizeof(thread_t));
  if (!ret)
    goto error;

  ret->reactor = reactor_new();
  if (!ret->reactor)
    goto error;

  ret->work_queue = fixed_queue_new(work_queue_capacity);
  if (!ret->work_queue)
    goto error;

  // Start is on the stack, but we use a semaphore, so it's safe
  struct start_arg start;
  start.start_sem = semaphore_new(0);
  if (!start.start_sem)
    goto error;

  strncpy(ret->name, name, THREAD_NAME_MAX);
  start.thread = ret;
  start.error = 0;
  pthread_create(&ret->pthread, NULL, run_thread, &start);
  semaphore_wait(start.start_sem);
  semaphore_free(start.start_sem);

  if (start.error)
    goto error;

  return ret;

error:;
  if (ret) {
    fixed_queue_free(ret->work_queue, osi_free);
    reactor_free(ret->reactor);
  }
  osi_free(ret);
  return NULL;
}
Example #9
0
/*******************************************************************************
**
** Function         avct_lcb_alloc
**
** Description      Allocate a link control block.
**
**
** Returns          pointer to the lcb, or NULL if none could be allocated.
**
*******************************************************************************/
tAVCT_LCB *avct_lcb_alloc(BD_ADDR bd_addr)
{
    tAVCT_LCB   *p_lcb = &avct_cb.lcb[0];
    int         i;

    for (i = 0; i < AVCT_NUM_LINKS; i++, p_lcb++) {
        if (!p_lcb->allocated) {
            p_lcb->allocated = (UINT8)(i + 1);
            memcpy(p_lcb->peer_addr, bd_addr, BD_ADDR_LEN);
            AVCT_TRACE_DEBUG("avct_lcb_alloc %d", p_lcb->allocated);
            p_lcb->tx_q = fixed_queue_new(SIZE_MAX);
            break;
        }
    }

    if (i == AVCT_NUM_LINKS) {
        /* out of lcbs */
        p_lcb = NULL;
        AVCT_TRACE_WARNING("Out of lcbs");
    }
    return p_lcb;
}
thread_t *thread_new(const char *name) {
  assert(name != NULL);

  // Start is on the stack, but we use a semaphore, so it's safe
  thread_t *ret = calloc(1, sizeof(thread_t));
  if (!ret)
    goto error;

  ret->reactor = reactor_new();
  if (!ret->reactor)
    goto error;

  ret->work_queue = fixed_queue_new(WORK_QUEUE_CAPACITY);
  if (!ret->work_queue)
    goto error;

  struct start_arg start;
  start.start_sem = semaphore_new(0);
  if (!start.start_sem)
    goto error;

  strncpy(ret->name, name, THREAD_NAME_MAX);
  start.thread = ret;
  start.error = 0;
  pthread_create(&ret->pthread, NULL, run_thread, &start);
  semaphore_wait(start.start_sem);
  semaphore_free(start.start_sem);
  if (start.error)
    goto error;
  return ret;

error:;
  if (ret) {
    fixed_queue_free(ret->work_queue, free);
    reactor_free(ret->reactor);
  }
  free(ret);
  return NULL;
}
Example #11
0
/******************************************************************************
**
** Function         bte_main_boot_entry
**
** Description      BTE MAIN API - Entry point for BTE chip/stack initialization
**
** Returns          None
**
******************************************************************************/
int bte_main_boot_entry(bluedroid_init_done_cb_t cb)
{
#ifdef CONFIG_BLUEDROID_MEM_DEBUG
    osi_mem_dbg_init();
#endif

    if (gki_init()) {
        LOG_ERROR("%s: Init GKI Module Failure.\n", __func__);
        return -1;
    }

    hci = hci_layer_get_interface();
    if (!hci) {
        LOG_ERROR("%s could not get hci layer interface.\n", __func__);
        return -2;
    }

    btu_hci_msg_queue = fixed_queue_new(SIZE_MAX);
    if (btu_hci_msg_queue == NULL) {
        LOG_ERROR("%s unable to allocate hci message queue.\n", __func__);
        return -3;
    }

    bluedroid_init_done_cb = cb;

    //Caution: No event dispatcher defined now in hci layer
    //data_dispatcher_register_default(hci->event_dispatcher, btu_hci_msg_queue);
    hci->set_data_queue(btu_hci_msg_queue);

#if (defined(BLE_INCLUDED) && (BLE_INCLUDED == TRUE))
    //bte_load_ble_conf(BTE_BLE_STACK_CONF_FILE);
#endif

    //Enbale HCI
    bte_main_enable();

    return 0;
}
Example #12
0
/*****************************************************************************
**
** Function         BTU_StartUp
**
** Description      Initializes the BTU control block.
**
**                  NOTE: Must be called before creating any tasks
**                      (RPC, BTU, HCIT, APPL, etc.)
**
** Returns          void
**
******************************************************************************/
void BTU_StartUp(void)
{
    memset (&btu_cb, 0, sizeof (tBTU_CB));
    btu_cb.trace_level = HCI_INITIAL_TRACE_LEVEL;

    btu_bta_msg_queue = fixed_queue_new(SIZE_MAX);
    if (btu_bta_msg_queue == NULL) {
        goto error_exit;
    }

    btu_general_alarm_hash_map = hash_map_new(BTU_GENERAL_ALARM_HASH_MAP_SIZE,
                                 hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL);
    if (btu_general_alarm_hash_map == NULL) {
        goto error_exit;
    }

    pthread_mutex_init(&btu_general_alarm_lock, NULL);

    btu_general_alarm_queue = fixed_queue_new(SIZE_MAX);
    if (btu_general_alarm_queue == NULL) {
        goto error_exit;
    }

    btu_oneshot_alarm_hash_map = hash_map_new(BTU_ONESHOT_ALARM_HASH_MAP_SIZE,
                                 hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL);
    if (btu_oneshot_alarm_hash_map == NULL) {
        goto error_exit;
    }

    pthread_mutex_init(&btu_oneshot_alarm_lock, NULL);

    btu_oneshot_alarm_queue = fixed_queue_new(SIZE_MAX);
    if (btu_oneshot_alarm_queue == NULL) {
        goto error_exit;
    }

    btu_l2cap_alarm_hash_map = hash_map_new(BTU_L2CAP_ALARM_HASH_MAP_SIZE,
                                            hash_function_pointer, NULL, (data_free_fn)osi_alarm_free, NULL);
    if (btu_l2cap_alarm_hash_map == NULL) {
        goto error_exit;
    }

    pthread_mutex_init(&btu_l2cap_alarm_lock, NULL);

    btu_l2cap_alarm_queue = fixed_queue_new(SIZE_MAX);
    if (btu_l2cap_alarm_queue == NULL) {
        goto error_exit;
    }

    xBtuQueue = xQueueCreate(BTU_QUEUE_NUM, sizeof(BtTaskEvt_t));
    xTaskCreatePinnedToCore(btu_task_thread_handler, BTU_TASK_NAME, BTU_TASK_STACK_SIZE, NULL, BTU_TASK_PRIO, &xBtuTaskHandle, 0);
    btu_task_post(SIG_BTU_START_UP);
    /*
        // Continue startup on bt workqueue thread.
        thread_post(bt_workqueue_thread, btu_task_start_up, NULL);
    */
    return;

error_exit:;
    LOG_ERROR("%s Unable to allocate resources for bt_workqueue", __func__);
    BTU_ShutDown();
}
Example #13
0
static future_t *start_up(void) {
  LOG_INFO(LOG_TAG, "%s", __func__);

  // The host is only allowed to send at most one command initially,
  // as per the Bluetooth spec, Volume 2, Part E, 4.4 (Command Flow Control)
  // This value can change when you get a command complete or command status event.
  command_credits = 1;
  firmware_is_configured = false;

  pthread_mutex_init(&commands_pending_response_lock, NULL);

  // TODO(armansito): cutils/properties.h is only being used to pull-in runtime
  // settings on Android. Remove this conditional include once we have a generic
  // way to obtain system properties. For now, always use the default timeout on
  // non-Android builds.
  period_ms_t startup_timeout_ms = DEFAULT_STARTUP_TIMEOUT_MS;

#if !defined(OS_GENERIC)
  // Grab the override startup timeout ms, if present.
  char timeout_prop[PROPERTY_VALUE_MAX];
  if (!property_get("bluetooth.enable_timeout_ms", timeout_prop, STRING_VALUE_OF(DEFAULT_STARTUP_TIMEOUT_MS))
      || (startup_timeout_ms = atoi(timeout_prop)) < 100)
    startup_timeout_ms = DEFAULT_STARTUP_TIMEOUT_MS;
#endif  // !defined(OS_GENERIC)

  startup_timer = non_repeating_timer_new(startup_timeout_ms, startup_timer_expired, NULL);
  if (!startup_timer) {
    LOG_ERROR(LOG_TAG, "%s unable to create startup timer.", __func__);
    goto error;
  }

  // Make sure we run in a bounded amount of time
  non_repeating_timer_restart(startup_timer);

  epilog_timer = non_repeating_timer_new(EPILOG_TIMEOUT_MS, epilog_timer_expired, NULL);
  if (!epilog_timer) {
    LOG_ERROR(LOG_TAG, "%s unable to create epilog timer.", __func__);
    goto error;
  }

  command_response_timer = non_repeating_timer_new(COMMAND_PENDING_TIMEOUT, command_timed_out, NULL);
  if (!command_response_timer) {
    LOG_ERROR(LOG_TAG, "%s unable to create command response timer.", __func__);
    goto error;
  }

  command_queue = fixed_queue_new(SIZE_MAX);
  if (!command_queue) {
    LOG_ERROR(LOG_TAG, "%s unable to create pending command queue.", __func__);
    goto error;
  }

  packet_queue = fixed_queue_new(SIZE_MAX);
  if (!packet_queue) {
    LOG_ERROR(LOG_TAG, "%s unable to create pending packet queue.", __func__);
    goto error;
  }

  thread = thread_new("hci_thread");
  if (!thread) {
    LOG_ERROR(LOG_TAG, "%s unable to create thread.", __func__);
    goto error;
  }

  commands_pending_response = list_new(NULL);
  if (!commands_pending_response) {
    LOG_ERROR(LOG_TAG, "%s unable to create list for commands pending response.", __func__);
    goto error;
  }

  memset(incoming_packets, 0, sizeof(incoming_packets));

  packet_fragmenter->init(&packet_fragmenter_callbacks);

  fixed_queue_register_dequeue(command_queue, thread_get_reactor(thread), event_command_ready, NULL);
  fixed_queue_register_dequeue(packet_queue, thread_get_reactor(thread), event_packet_ready, NULL);

  vendor->open(btif_local_bd_addr.address, &interface);
  hal->init(&hal_callbacks, thread);
  low_power_manager->init(thread);

  vendor->set_callback(VENDOR_CONFIGURE_FIRMWARE, firmware_config_callback);
  vendor->set_callback(VENDOR_CONFIGURE_SCO, sco_config_callback);
  vendor->set_callback(VENDOR_DO_EPILOG, epilog_finished_callback);

  if (!hci_inject->open(&interface)) {
    // TODO(sharvil): gracefully propagate failures from this layer.
  }

  int power_state = BT_VND_PWR_OFF;
#if (defined (BT_CLEAN_TURN_ON_DISABLED) && BT_CLEAN_TURN_ON_DISABLED == TRUE)
  LOG_WARN(LOG_TAG, "%s not turning off the chip before turning on.", __func__);
  // So apparently this hack was needed in the past because a Wingray kernel driver
  // didn't handle power off commands in a powered off state correctly.

  // The comment in the old code said the workaround should be removed when the
  // problem was fixed. Sadly, I have no idea if said bug was fixed or if said
  // kernel is still in use, so we must leave this here for posterity. #sadpanda
#else
  // cycle power on the chip to ensure it has been reset
  vendor->send_command(VENDOR_CHIP_POWER_CONTROL, &power_state);
#endif
  power_state = BT_VND_PWR_ON;
  vendor->send_command(VENDOR_CHIP_POWER_CONTROL, &power_state);

  startup_future = future_new();
  LOG_DEBUG(LOG_TAG, "%s starting async portion", __func__);
  thread_post(thread, event_finish_startup, NULL);
  return startup_future;
error:;
  shut_down(); // returns NULL so no need to wait for it
  return future_new_immediate(FUTURE_FAIL);
}