蓝牙重启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); break; case 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掉当前的进程,也就是重启蓝牙进程。