蓝牙重启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 ...
随机推荐
- css设计并排布局
css code form#reset_password ul { list-style: none; margin: 0 0 20px 200px; padding:; } form#reset_p ...
- 白盒测试实践-DAY1
时间:2017.12.11 地点:软件学院 成员:张玉.周静.张双双 会议内容:讨论题目要求,分配任务 针对第一阶段的任务进行部署,共同学习白盒测试方法,根据自己选择的系统--餐厅网站,针对其中的管理 ...
- eclipse neon配置tomcat8无法显示默认页面解决方法
下载对应tomcat8版本到本地后,在eclipse中添加tomcat8的对应目录,输入http://localhost:8080时无法显示tomcat的index.jsp页面(会显示404页面).原 ...
- MySQL隐形索引简介
不可见索引允许您将索引标记为查询优化器不可用.MySQL维护不可见索引,并在与索引关联的列中的数据发生更改时使其保持最新. 默认情况下,索引是可见的.要使它们不可见,您必须在创建时或使用ALTER T ...
- ZooKeeper 集群的安装部署
0. 说明 ZooKeeper 安装在 s102.s103.s104上,这三个节点同时是 Hadoop 的 DataNode 1. ZooKeeper 本地模式安装配置 1.0 在 s101 上进行安 ...
- Hadoop HBase概念学习系列之HBase里的HRegion(五)
首先,要区分,HRegion服务器包含两大部分:HLog部分和HRegion部分 HBase里的HRegion服务器 HBase里的HRegion 当表的大小超过设置值的时候,HBase会自动将表划 ...
- 搭建spark集群
搭建spark集群 spark1.6和hadoop2.61.准备hadoop环境:2.准备下载包:3.解压安装包:tar -xf spark-1.6.0-bin-hadoop2.6.tgz4.修改配置 ...
- Java AWT
AWT是抽象窗口工具包,是API为Java 程序提供的建立图形用户界面GUI (Graphics User Interface)工具集,AWT可用于Java的applet和applications中. ...
- 展示博客(Alpha版本)
小队名称:PHILOSOPHER 小组成员 [组长]金盛昌(201421122043).刘文钊(20142112255).陈笑林(201421122042) 张俊逸(201421122044).陈志建 ...
- [部署]VM11下CentOS7mini安装及配置
最近使用了CentOS发现比Ubuntu更简洁,有些爱上CentOS了 1. 准备一版CentOS安装镜像文件 官网下载地址:http://www.centos.org/download/ 官方有三个 ...