蓝牙的通信分为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. leetCode题解之Longest Palindrome

    1.题目描述 2.问题分析 直接用hash table 做就行. 3.代码 int longestPalindrome(string s) { ) ; map<char,int> m; f ...

  2. python数据类型之间的转换

    1,字符串转整型,前提条件是该字符串为纯数字. a = '1' a = int(a) 2,整型转字符串 a= 1 a = str(a) 3,整型转浮点型 a = 1 a = float(a) 4,浮点 ...

  3. 什么是 Azure 中的虚拟机规模集?

    虚拟机规模集是一种 Azure 计算资源,可用于部署和管理一组相同的 VM. 由于所有 VM 的配置都相同,因此无需对 VM 进行任何预先配置. 这样就可以更方便地构建面向大型计算.大数据.容器化工作 ...

  4. Jmeter之HTTP Cookie 管理器

    Jmeter所支持的Cookie标准有很多,同时jmeter也提供两组程序实现这些cookie标准,分别是httpclient3与httpclient4.http cookie 管理器中的Implem ...

  5. lock 相关

    lock基本思路: volitile + CAS +Queue(存放线程)   实现了:   1 可见性(volitile 和  happenedBefor原则共同实现) 与  2 原子性(CAS , ...

  6. mysql中InnoDB表为什么要建议用自增列做主键

    InnoDB引擎表的特点 1.InnoDB引擎表是基于B+树的索引组织表(IOT) 关于B+树 (图片来源于网上) B+ 树的特点: (1)所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关 ...

  7. git常用命令图

  8. ping 命令 指定特定网卡 发送 ICMP 数据包

    Windows : ping -S Linux : ping -I <device> -I interface interface is either an address, or an ...

  9. 恶意代码分析_01_YARA规则_CLAMAV病毒库

    写在前面的话: 上一篇文章里,我们已经初步了解了Malware的一些知识,并且利用Clamscan创建了自己的md5类型的病毒库, 那在这篇文章中,我将带领大家一起,来进一步了解病毒库的相关知识,以及 ...

  10. Vue.js Is Good, but Is It Better Than Angular or React?

    Vue.js is a JavaScript library for building web interfaces. Combining  with some other tools It also ...