蓝牙的通信分为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的更多相关文章

  1. [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 ...

  2. 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 ...

  3. 虚拟机非正常关闭,里面的服务器重启报错:Error, some other host already uses address

    解决办法: vi /etc/sysconfig/network-scripts/ifup-eth ###########注销下面的三行内容############ # if ! /sbin/arpin ...

  4. redhat系统服务器重启后提示An error occurred during the file system check.

    问题描述 浪潮一台NF8480M3外观红灯报警,鉴于无法登陆带外,只能对服务器进行断电重启操作 问题现象 重启后进入开机过程并报错,报错如下内容及图片如下所示,正常来说进入此界面后直接输入root密码 ...

  5. linux系统重启后提示An error occurred during the file system check.

    一.问题描述 生产环境中一台浪潮NF8480M3外观红灯报警,鉴于无法登陆带外管理口,只能对服务器进行断电重启操作 二.问题现象 重启后进入开机过程并报错,正常来说进入此界面后直接输入root密码即可 ...

  6. Rochester Memory Hardware Error Research Project

    http://www.cs.rochester.edu/research/os/memerror/

  7. yarn关于app max attempt深度解析,针对长服务appmaster平滑重启

    在YARN上开发长服务,需要注意fault-tolerance,本篇文章对appmaster的平滑重启的一个参数做了解析,如何设置可以有助于达到appmaster平滑重启. 在yarn-site.xm ...

  8. iOS - Bluetooth 蓝牙

    1.蓝牙介绍 具体讲解见 蓝牙 技术信息 蓝牙协议栈 2.iBeacon 具体讲解见 Beacon iBeacon 是苹果公司 2013 年 9 月发布的移动设备用 OS(iOS7)上配备的新功能.其 ...

  9. System Error Codes

    很明显,以下的文字来自微软MSDN 链接http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx M ...

随机推荐

  1. css设计并排布局

    css code form#reset_password ul { list-style: none; margin: 0 0 20px 200px; padding:; } form#reset_p ...

  2. 白盒测试实践-DAY1

    时间:2017.12.11 地点:软件学院 成员:张玉.周静.张双双 会议内容:讨论题目要求,分配任务 针对第一阶段的任务进行部署,共同学习白盒测试方法,根据自己选择的系统--餐厅网站,针对其中的管理 ...

  3. eclipse neon配置tomcat8无法显示默认页面解决方法

    下载对应tomcat8版本到本地后,在eclipse中添加tomcat8的对应目录,输入http://localhost:8080时无法显示tomcat的index.jsp页面(会显示404页面).原 ...

  4. MySQL隐形索引简介

    不可见索引允许您将索引标记为查询优化器不可用.MySQL维护不可见索引,并在与索引关联的列中的数据发生更改时使其保持最新. 默认情况下,索引是可见的.要使它们不可见,您必须在创建时或使用ALTER T ...

  5. ZooKeeper 集群的安装部署

    0. 说明 ZooKeeper 安装在 s102.s103.s104上,这三个节点同时是 Hadoop 的 DataNode 1. ZooKeeper 本地模式安装配置 1.0 在 s101 上进行安 ...

  6. Hadoop HBase概念学习系列之HBase里的HRegion(五)

    首先,要区分,HRegion服务器包含两大部分:HLog部分和HRegion部分 HBase里的HRegion服务器  HBase里的HRegion 当表的大小超过设置值的时候,HBase会自动将表划 ...

  7. 搭建spark集群

    搭建spark集群 spark1.6和hadoop2.61.准备hadoop环境:2.准备下载包:3.解压安装包:tar -xf spark-1.6.0-bin-hadoop2.6.tgz4.修改配置 ...

  8. Java AWT

    AWT是抽象窗口工具包,是API为Java 程序提供的建立图形用户界面GUI (Graphics User Interface)工具集,AWT可用于Java的applet和applications中. ...

  9. 展示博客(Alpha版本)

    小队名称:PHILOSOPHER 小组成员 [组长]金盛昌(201421122043).刘文钊(20142112255).陈笑林(201421122042) 张俊逸(201421122044).陈志建 ...

  10. [部署]VM11下CentOS7mini安装及配置

    最近使用了CentOS发现比Ubuntu更简洁,有些爱上CentOS了 1. 准备一版CentOS安装镜像文件 官网下载地址:http://www.centos.org/download/ 官方有三个 ...