蓝牙重启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 + ) + 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, , 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 >> );
- /* 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(); /* 100milliseconds */
- /* Killing the process to force a restart as part of fault tolerance */
- kill(getpid(), SIGKILL);
- break;
- ...
- }
这个函数btif_dm_upstreams_evt 就是上报event 事件到 btif层面。我们发现最终的处理是kill掉当前的进程,也就是重启蓝牙进程。
蓝牙重启case之:hardware error的更多相关文章
- [Firmware Warn]: GHES: Failed to read error status block address for hardware error source
Firmware Warn 问题描述: 系统版本:Ubuntu 12.04 LTS. 系统启动后dmesg打印大量Firmware Warn告警信息到syslog文件中.信息如下: [Firmware ...
- MyEclipse10.7安装Aptana后重启:An internal error has occurred. No more handles [Could not detect registered XULRunner to use]
问题描述: 当安装Aptana插件后重启MyEclipse10.7,发生错误: An internal error has occurred. No more handles [Could not d ...
- 虚拟机非正常关闭,里面的服务器重启报错:Error, some other host already uses address
解决办法: vi /etc/sysconfig/network-scripts/ifup-eth ###########注销下面的三行内容############ # if ! /sbin/arpin ...
- redhat系统服务器重启后提示An error occurred during the file system check.
问题描述 浪潮一台NF8480M3外观红灯报警,鉴于无法登陆带外,只能对服务器进行断电重启操作 问题现象 重启后进入开机过程并报错,报错如下内容及图片如下所示,正常来说进入此界面后直接输入root密码 ...
- linux系统重启后提示An error occurred during the file system check.
一.问题描述 生产环境中一台浪潮NF8480M3外观红灯报警,鉴于无法登陆带外管理口,只能对服务器进行断电重启操作 二.问题现象 重启后进入开机过程并报错,正常来说进入此界面后直接输入root密码即可 ...
- Rochester Memory Hardware Error Research Project
http://www.cs.rochester.edu/research/os/memerror/
- yarn关于app max attempt深度解析,针对长服务appmaster平滑重启
在YARN上开发长服务,需要注意fault-tolerance,本篇文章对appmaster的平滑重启的一个参数做了解析,如何设置可以有助于达到appmaster平滑重启. 在yarn-site.xm ...
- iOS - Bluetooth 蓝牙
1.蓝牙介绍 具体讲解见 蓝牙 技术信息 蓝牙协议栈 2.iBeacon 具体讲解见 Beacon iBeacon 是苹果公司 2013 年 9 月发布的移动设备用 OS(iOS7)上配备的新功能.其 ...
- System Error Codes
很明显,以下的文字来自微软MSDN 链接http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx M ...
随机推荐
- Python+Selenium笔记(一):环境配置+简单的例子
#环境配置基于windows操作系统 #学习selenium要有一些HTML和xpth的基础,完全不会的建议先花点时间学点基础(不然元素定位,特别是xpth可能看的有点懵) #HTML : http ...
- 控制台中寄宿WCF服务
一.首先创建一个类库,用来定义WCF服务 修改服务代码定义,具体代码如下 // 注意: 使用"重构"菜单上的"重命名"命令,可以同时更改代码和配置文件中的接口名 ...
- LeetCode 题解之 Positions of Large Groups
1.题目描述 2.问题分析 从头遍历字符串,使用一个局部迭代器和局部变量记录该字符个数.如果个数>= 3 ,则将此时的迭代器位置和局部迭代器的位置保存到局部vector中.再将这个局部vecto ...
- .net core项目初建
电脑装Visual Studio2017,并升级版本.启动一个.net core 的项目. NET Core基本介绍 1.1 什么是ASP.NET Core ASP.NET Core 是一个全新的开源 ...
- ndk的注意事项
从开源网站下载的源码,需要自己编译c源码成so类库.当时用Android studio 运行总是报错"finished with non-zero exit value 2"报错定 ...
- Python中的分组函数(groupby、itertools)
from operator import itemgetter #itemgetter用来去dict中的key,省去了使用lambda函数 from itertools import groupby ...
- SQL Server:INFORMATION_SCHEMA.columns 与sys.columns 与 syscolumns对比
sys.columns视图 sys.columns是SQL Server从2005版本起引入的新的系统级视图.相关链接如下: Mapping SQL Server 2000 System Tables ...
- 百度地图POI数据爬取,突破百度地图API爬取数目“400条“的限制11。
1.POI爬取方法说明 1.1AK申请 登录百度账号,在百度地图开发者平台的API控制台申请一个服务端的ak,主要用到的是Place API.检校方式可设置成IP白名单,IP直接设置成了0.0.0.0 ...
- 命令行翻译 推荐一个linux系统中可用的终端小程序
程序的github地址:https://github.com/fanbrightup/fanyi 使用起来非常简单,同时支持中英文互译甚至是整句. 步骤一:首先你需要安装node,参见我的node安装 ...
- ethjs-1-了解
https://github.com/ethjs/ethjs/blob/master/docs/user-guide.md Install npm install --save ethjs Usage ...