蓝牙重启case之:hardware error

蓝牙的通信分为host和controller,host端发送数据和命令到controller,controller 上传event以及数据到host端,这要求上下两端的通信要求状态一致性。

当发生状态不一致的时候,Bluetooth进程应该有预案去重新初始化蓝牙。

这篇文章就介绍一种case,当控制端出现hardware error 的时候,host是如何处理此case的,结果就是host会重新启动蓝牙进程,现在我们简单分析其流程。

在之前的 hci 消息的处理线程中,我们已经知道,底层上传的数据都会塞到btu_hci_msg_queue 这个队列,我们看下协议栈是如何来处理这个队列中的数据:

  fixed_queue_register_dequeue(btu_hci_msg_queue,
      thread_get_reactor(bt_workqueue_thread),
      btu_hci_msg_ready,//使用该函数处理
      NULL);
void btu_hci_msg_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) {
    BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);//数据出列
    btu_hci_msg_process(p_msg);//处理数据
}

 

我们可以看出btu_hci_msg_process 是用来处理数据的,这里我们就看看其是如何处理hci event的:

static void btu_hci_msg_process(BT_HDR *p_msg) {
    /* Determine the input message type. */
    switch (p_msg->event & BT_EVT_MASK)
    {
...
        case BT_EVT_TO_BTU_HCI_ACL:
            /* All Acl Data goes to L2CAP */
            l2c_rcv_acl_data (p_msg);
            breakcase BT_EVT_TO_BTU_HCI_EVT:
            btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
            GKI_freebuf(p_msg);
...
    }

}

 

处理函数是btu_hcif_process_event 

void btu_hcif_process_event (UNUSED_ATTR UINT8 controller_id, BT_HDR *p_msg)
{
    UINT8   *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
    UINT8   hci_evt_code, hci_evt_len;
    STREAM_TO_UINT8  (hci_evt_code, p);
    STREAM_TO_UINT8  (hci_evt_len, p);

    switch (hci_evt_code)
    {
        case HCI_INQUIRY_COMP_EVT:
            btu_hcif_inquiry_comp_evt (p);
            break;
        case HCI_INQUIRY_RESULT_EVT:
            btu_hcif_inquiry_result_evt (p);
            break;
        case HCI_INQUIRY_RSSI_RESULT_EVT:
            btu_hcif_inquiry_rssi_result_evt (p);
...
         case HCI_HARDWARE_ERROR_EVT:
            btu_hcif_hardware_error_evt (p);//处理hardware 错误的函数
            break;    
...

 

static void btu_hcif_hardware_error_evt (UINT8 *p)
{
    /* If anyone wants device status notifications, give him one. */
    btm_report_device_status (BTM_DEV_STATUS_DOWN);//向上层汇报状态
    /* Reset the controller */
    if (BTM_IsDeviceUp())
        BTM_DeviceReset (NULL);
}

btm_report_device_status (BTM_DEV_STATUS_DOWN); 这个函数里面是调用btm的callback,

/*******************************************************************************
**
** Function         btm_report_device_status
**
** Description      This function is called when there is a change in the device
**                  status. This function will report the new device status to
**                  the application
**
** Returns          void
**
*******************************************************************************/
void btm_report_device_status (tBTM_DEV_STATUS status)
{
    tBTM_DEV_STATUS_CB *p_cb = btm_cb.devcb.p_dev_status_cb;

    /* Call the call back to pass the device status to application */
    if (p_cb)
        (*p_cb)(status);
}

 

看注释知道是当有设备状态发生改变的时候,会将这个消息发送给应用层。

那可以想象,这些状态通过回调函数一层一层往上调用,最终会通知到应用层,那么首先看看btm_cb.devcb.p_dev_status_cb 是在哪里注册。这里搜索了一下代码发现是在bta_sys_init里面:

/*******************************************************************************
**
** Function         bta_sys_init
**
** Description      BTA initialization; called from task initialization.
**
**
** Returns          void
**
*******************************************************************************/
void bta_sys_init(void)
{
    memset(&bta_sys_cb, 0, sizeof(tBTA_SYS_CB));
...

  /* register BTA SYS message handler */
  bta_sys_register( BTA_ID_SYS, &bta_sys_hw_reg);

/* register for BTM notifications */
    BTM_RegisterForDeviceStatusNotif ((tBTM_DEV_STATUS_CB*)&bta_sys_hw_btm_cback ); 

那我们知道btm_cb.devcb.p_dev_status_cb = bta_sys_hw_btm_cback

并且我们知道BTA_ID_SYS 对应的处理 handler 是bta_sys_hw_reg

static const tBTA_SYS_REG bta_sys_hw_reg =
{
    bta_sys_sm_execute,
    NULL
};

 

/*******************************************************************************
**
** Function         bta_sys_hw_btm_cback
**
** Description     This function is registered by BTA SYS to BTM in order to get status notifications
**
**
** Returns
**
*******************************************************************************/
void bta_sys_hw_btm_cback( tBTM_DEV_STATUS status )
{
    tBTA_SYS_HW_MSG *sys_event;
    /* send a message to BTA SYS */
    if ((sys_event = (tBTA_SYS_HW_MSG *) GKI_getbuf(sizeof(tBTA_SYS_HW_MSG))) != NULL)
    {
        if (status == BTM_DEV_STATUS_UP)
            sys_event->hdr.event = BTA_SYS_EVT_STACK_ENABLED_EVT;
        else if (status == BTM_DEV_STATUS_DOWN)
            sys_event->hdr.event = BTA_SYS_ERROR_EVT;//向bta发送此消息
        else
        {
            /* BTM_DEV_STATUS_CMD_TOUT is ignored for now. */
            GKI_freebuf (sys_event);
            sys_event = NULL;
        }
        if (sys_event)
        {
            bta_sys_sendmsg(sys_event);//向bta 发送消息
        }
    }
}

 

 可以看出这个回调函数只是向BTA 模块发送event = BTA_SYS_ERROR_EVT

我们接着看其处理流程:

/* events sent to SYS HW manager - must be kept synchronized with tables in bta_sys_main.c  与该文件保持一致*/ 
enum
{
    /* device manager local device API events */
    BTA_SYS_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_SYS),//sys id = 0
    BTA_SYS_EVT_ENABLED_EVT,
    BTA_SYS_EVT_STACK_ENABLED_EVT,
    BTA_SYS_API_DISABLE_EVT,
    BTA_SYS_EVT_DISABLED_EVT,
    BTA_SYS_ERROR_EVT,

    BTA_SYS_MAX_EVT
};

 

 现在简单看下 bta_sys_sendmsg 的数据走向:

  fixed_queue_register_dequeue(btu_bta_msg_queue,
      thread_get_reactor(bt_workqueue_thread),
      btu_bta_msg_ready, //当队列有数据,使用这个函数处理
      NULL);
void btu_bta_msg_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) {
    BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);//数据出列
    bta_sys_event(p_msg);//处理数据
}

 从上面我们可以知道,处理数据的函数就是bta_sys_event,但是这个函数并不是最终的每个msg的处理函数,它只是一个中介,它会将各个消息按照类型分发到不同的具体的处理函数:

void bta_sys_event(BT_HDR *p_msg)
{
    UINT8       id;
    BOOLEAN     freebuf = TRUE;
    /* get subsystem id from event */
    id = (UINT8) (p_msg->event >> 8);

    /* verify id and call subsystem event handler */
    if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL))
    {
        freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
    }
}

 从上面的代码可以看出其实际调用(*bta_sys_cb.reg[id]->evt_hdlr)(p_msg) 来处理消息的,其寻找处理函数是根据 id作为index 来搜索的,那我们很容易想到,这个函数肯定是之前已经注册好的。

不同的event 会由不同的handler来处理。

 在bta_sys_init中,我们已经知道BTA_ID_SYS 对应的处理 handler 是bta_sys_hw_reg,我们看看其具体的处理:

虽然是经过状态机来轮转处理的,但是最终执行的action是:BTA_SYS_HW_ERROR,对应的实现:

void bta_sys_hw_error(tBTA_SYS_HW_MSG *p_sys_hw_msg)
{
...
                case BTA_SYS_HW_BLUETOOTH:
                   /* Send BTA_SYS_HW_ERROR_EVT to DM */
                   if (bta_sys_cb.sys_hw_cback[module_index] != NULL)
                       bta_sys_cb.sys_hw_cback[module_index] (BTA_SYS_HW_ERROR_EVT);
...
}

在bta_dm_enable函数中已经注册了sys_hw_cback:

    /* first, register our callback to SYS HW manager */
    bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback );
/*******************************************************************************
**
** Function         bta_dm_sys_hw_cback
**
** Description     callback register to SYS to get HW status updates
**
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status )
{
    DEV_CLASS   dev_class;
    tBTA_DM_SEC_CBACK           *temp_cback;

    /* On H/W error evt, report to the registered DM application callback */
    if (status == BTA_SYS_HW_ERROR_EVT) {
          if( bta_dm_cb.p_sec_cback != NULL )
                bta_dm_cb.p_sec_cback(BTA_DM_HW_ERROR_EVT, NULL);
          return;
    }
...
}

 

这里发现,又调用了bta_dm_cb.p_sec_cback(BTA_DM_HW_ERROR_EVT, NULL);

这个函数的回调其实是调用到了btif线程(JNI线程)

void btif_init_ok(UNUSED_ATTR uint16_t event, UNUSED_ATTR char *p_param) {
  BTIF_TRACE_DEBUG("btif_task: received trigger stack init event");
#if (BLE_INCLUDED == TRUE)
  btif_dm_load_ble_local_keys();
#endif
  BTA_EnableBluetooth(bte_dm_evt);//bta_dm_cb.p_sec_cback= bte_dm_evt
}

 

看看这个bte_dm_evt:

/*******************************************************************************
**
** Function         bte_dm_evt
**
** Description      Switches context from BTE to BTIF for all DM events
**
** Returns          void
**
*******************************************************************************/

void bte_dm_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *p_data)
{
    /* switch context to btif task context (copy full union size for convenience) */
    bt_status_t status = btif_transfer_context(btif_dm_upstreams_evt, (uint16_t)event,
                                (void*)p_data, sizeof(tBTA_DM_SEC), btif_dm_data_copy);//将函数btif_dm_upstreams_evt transfer 到btif线程执行,也即是向btif线程发送消息
    /* catch any failed context transfers */
    ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
}

 

 那实际处理的函数就是btif_dm_upstreams_evt ,其参数是BTA_DM_HW_ERROR_EVT

static void btif_dm_upstreams_evt(UINT16 event, char* p_param){
...
 case BTA_DM_HW_ERROR_EVT:
            BTIF_TRACE_ERROR("Received H/W Error. ");
            /* Flush storage data */
            btif_config_flush();
            usleep(100000); /* 100milliseconds */
            /* Killing the process to force a restart as part of fault tolerance */
            kill(getpid(), SIGKILL);
            break;
...
}

 

这个函数btif_dm_upstreams_evt 就是上报event 事件到 btif层面。我们发现最终的处理是kill掉当前的进程,也就是重启蓝牙进程。

 

posted @ 2018-06-22 17:03  雪山飞燕  阅读(1906)  评论(0编辑  收藏  举报