蓝牙的通信分为host和controller,host端发送数据和命令到controller,controller 上传event以及数据到host端,这要求上下两端的通信要求状态一致性。

当发生状态不一致的时候,Bluetooth进程应该有预案去重新初始化蓝牙。

这篇文章就介绍一种case,当控制端出现hardware error 的时候,host是如何处理此case的,结果就是host会重新启动蓝牙进程,现在我们简单分析其流程。

在之前的 hci 消息的处理线程中,我们已经知道,底层上传的数据都会塞到btu_hci_msg_queue 这个队列,我们看下协议栈是如何来处理这个队列中的数据:

  1. fixed_queue_register_dequeue(btu_hci_msg_queue,
  2. thread_get_reactor(bt_workqueue_thread),
  3. btu_hci_msg_ready,//使用该函数处理
  4. NULL);
  1. void btu_hci_msg_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) {
  2. BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);//数据出列
  3. btu_hci_msg_process(p_msg);//处理数据
  4. }

我们可以看出btu_hci_msg_process 是用来处理数据的,这里我们就看看其是如何处理hci event的:

  1. static void btu_hci_msg_process(BT_HDR *p_msg) {
  2. /* Determine the input message type. */
  3. switch (p_msg->event & BT_EVT_MASK)
  4. {
  5. ...
  6. case BT_EVT_TO_BTU_HCI_ACL:
  7. /* All Acl Data goes to L2CAP */
  8. l2c_rcv_acl_data (p_msg);
  9. break
  10.  
  11. case BT_EVT_TO_BTU_HCI_EVT:
  12. btu_hcif_process_event ((UINT8)(p_msg->event & BT_SUB_EVT_MASK), p_msg);
  13. GKI_freebuf(p_msg);
  14. ...
  15. }
  16.  
  17. }

处理函数是btu_hcif_process_event

  1. void btu_hcif_process_event (UNUSED_ATTR UINT8 controller_id, BT_HDR *p_msg)
  2. {
  3. UINT8 *p = (UINT8 *)(p_msg + ) + p_msg->offset;
  4. UINT8 hci_evt_code, hci_evt_len;
  5. STREAM_TO_UINT8 (hci_evt_code, p);
  6. STREAM_TO_UINT8 (hci_evt_len, p);
  7.  
  8. switch (hci_evt_code)
  9. {
  10. case HCI_INQUIRY_COMP_EVT:
  11. btu_hcif_inquiry_comp_evt (p);
  12. break;
  13. case HCI_INQUIRY_RESULT_EVT:
  14. btu_hcif_inquiry_result_evt (p);
  15. break;
  16. case HCI_INQUIRY_RSSI_RESULT_EVT:
  17. btu_hcif_inquiry_rssi_result_evt (p);
  18. ...
  19. case HCI_HARDWARE_ERROR_EVT:
  20. btu_hcif_hardware_error_evt (p);//处理hardware 错误的函数
  21. break;
  22. ...
  1. static void btu_hcif_hardware_error_evt (UINT8 *p)
  2. {
  3. /* If anyone wants device status notifications, give him one. */
  4. btm_report_device_status (BTM_DEV_STATUS_DOWN);//向上层汇报状态
  5. /* Reset the controller */
  6. if (BTM_IsDeviceUp())
  7. BTM_DeviceReset (NULL);
  8. }

btm_report_device_status (BTM_DEV_STATUS_DOWN); 这个函数里面是调用btm的callback,

  1. /*******************************************************************************
  2. **
  3. ** Function btm_report_device_status
  4. **
  5. ** Description This function is called when there is a change in the device
  6. ** status. This function will report the new device status to
  7. ** the application
  8. **
  9. ** Returns void
  10. **
  11. *******************************************************************************/
  12. void btm_report_device_status (tBTM_DEV_STATUS status)
  13. {
  14. tBTM_DEV_STATUS_CB *p_cb = btm_cb.devcb.p_dev_status_cb;
  15.  
  16. /* Call the call back to pass the device status to application */
  17. if (p_cb)
  18. (*p_cb)(status);
  19. }

看注释知道是当有设备状态发生改变的时候,会将这个消息发送给应用层。

那可以想象,这些状态通过回调函数一层一层往上调用,最终会通知到应用层,那么首先看看btm_cb.devcb.p_dev_status_cb 是在哪里注册。这里搜索了一下代码发现是在bta_sys_init里面:

  1. /*******************************************************************************
  2. **
  3. ** Function bta_sys_init
  4. **
  5. ** Description BTA initialization; called from task initialization.
  6. **
  7. **
  8. ** Returns void
  9. **
  10. *******************************************************************************/
  11. void bta_sys_init(void)
  12. {
  13. memset(&bta_sys_cb, , sizeof(tBTA_SYS_CB));
  14. ...

  /* register BTA SYS message handler */
  bta_sys_register( BTA_ID_SYS, &bta_sys_hw_reg);

  1. /* register for BTM notifications */
  2. 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

  1. static const tBTA_SYS_REG bta_sys_hw_reg =
  2. {
  3. bta_sys_sm_execute,
  4. NULL
  5. };
  1. /*******************************************************************************
  2. **
  3. ** Function bta_sys_hw_btm_cback
  4. **
  5. ** Description This function is registered by BTA SYS to BTM in order to get status notifications
  6. **
  7. **
  8. ** Returns
  9. **
  10. *******************************************************************************/
  11. void bta_sys_hw_btm_cback( tBTM_DEV_STATUS status )
  12. {
  13. tBTA_SYS_HW_MSG *sys_event;
  14. /* send a message to BTA SYS */
  15. if ((sys_event = (tBTA_SYS_HW_MSG *) GKI_getbuf(sizeof(tBTA_SYS_HW_MSG))) != NULL)
  16. {
  17. if (status == BTM_DEV_STATUS_UP)
  18. sys_event->hdr.event = BTA_SYS_EVT_STACK_ENABLED_EVT;
  19. else if (status == BTM_DEV_STATUS_DOWN)
  20. sys_event->hdr.event = BTA_SYS_ERROR_EVT;//向bta发送此消息
  21. else
  22. {
  23. /* BTM_DEV_STATUS_CMD_TOUT is ignored for now. */
  24. GKI_freebuf (sys_event);
  25. sys_event = NULL;
  26. }
  27. if (sys_event)
  28. {
  29. bta_sys_sendmsg(sys_event);//向bta 发送消息
  30. }
  31. }
  32. }

可以看出这个回调函数只是向BTA 模块发送event = BTA_SYS_ERROR_EVT

我们接着看其处理流程:

  1. /* events sent to SYS HW manager - must be kept synchronized with tables in bta_sys_main.c 与该文件保持一致*/
  2. enum
  3. {
  4. /* device manager local device API events */
  5. BTA_SYS_API_ENABLE_EVT = BTA_SYS_EVT_START(BTA_ID_SYS),//sys id = 0
  6. BTA_SYS_EVT_ENABLED_EVT,
  7. BTA_SYS_EVT_STACK_ENABLED_EVT,
  8. BTA_SYS_API_DISABLE_EVT,
  9. BTA_SYS_EVT_DISABLED_EVT,
  10. BTA_SYS_ERROR_EVT,
  11.  
  12. BTA_SYS_MAX_EVT
  13. };

现在简单看下 bta_sys_sendmsg 的数据走向:

  1. fixed_queue_register_dequeue(btu_bta_msg_queue,
  2. thread_get_reactor(bt_workqueue_thread),
  3. btu_bta_msg_ready, //当队列有数据,使用这个函数处理
  4. NULL);
  1. void btu_bta_msg_ready(fixed_queue_t *queue, UNUSED_ATTR void *context) {
  2. BT_HDR *p_msg = (BT_HDR *)fixed_queue_dequeue(queue);//数据出列
  3. bta_sys_event(p_msg);//处理数据
  4. }

从上面我们可以知道,处理数据的函数就是bta_sys_event,但是这个函数并不是最终的每个msg的处理函数,它只是一个中介,它会将各个消息按照类型分发到不同的具体的处理函数:

  1. void bta_sys_event(BT_HDR *p_msg)
  2. {
  3. UINT8 id;
  4. BOOLEAN freebuf = TRUE;
  5. /* get subsystem id from event */
  6. id = (UINT8) (p_msg->event >> );
  7.  
  8. /* verify id and call subsystem event handler */
  9. if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL))
  10. {
  11. freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
  12. }
  13. }

从上面的代码可以看出其实际调用(*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,对应的实现:

  1. void bta_sys_hw_error(tBTA_SYS_HW_MSG *p_sys_hw_msg)
  2. {
  3. ...
  4. case BTA_SYS_HW_BLUETOOTH:
  5. /* Send BTA_SYS_HW_ERROR_EVT to DM */
  6. if (bta_sys_cb.sys_hw_cback[module_index] != NULL)
  7. bta_sys_cb.sys_hw_cback[module_index] (BTA_SYS_HW_ERROR_EVT);
  8. ...
  9. }

在bta_dm_enable函数中已经注册了sys_hw_cback:

  1. /* first, register our callback to SYS HW manager */
  2. bta_sys_hw_register( BTA_SYS_HW_BLUETOOTH, bta_dm_sys_hw_cback );
  1. /*******************************************************************************
  2. **
  3. ** Function bta_dm_sys_hw_cback
  4. **
  5. ** Description callback register to SYS to get HW status updates
  6. **
  7. **
  8. ** Returns void
  9. **
  10. *******************************************************************************/
  11. static void bta_dm_sys_hw_cback( tBTA_SYS_HW_EVT status )
  12. {
  13. DEV_CLASS dev_class;
  14. tBTA_DM_SEC_CBACK *temp_cback;
  15.  
  16. /* On H/W error evt, report to the registered DM application callback */
  17. if (status == BTA_SYS_HW_ERROR_EVT) {
  18. if( bta_dm_cb.p_sec_cback != NULL )
  19. bta_dm_cb.p_sec_cback(BTA_DM_HW_ERROR_EVT, NULL);
  20. return;
  21. }
  22. ...
  23. }

这里发现,又调用了bta_dm_cb.p_sec_cback(BTA_DM_HW_ERROR_EVT, NULL);

这个函数的回调其实是调用到了btif线程(JNI线程)

  1. void btif_init_ok(UNUSED_ATTR uint16_t event, UNUSED_ATTR char *p_param) {
  2. BTIF_TRACE_DEBUG("btif_task: received trigger stack init event");
  3. #if (BLE_INCLUDED == TRUE)
  4. btif_dm_load_ble_local_keys();
  5. #endif
  6. BTA_EnableBluetooth(bte_dm_evt);//bta_dm_cb.p_sec_cback= bte_dm_evt
  7. }

看看这个bte_dm_evt:

  1. /*******************************************************************************
  2. **
  3. ** Function bte_dm_evt
  4. **
  5. ** Description Switches context from BTE to BTIF for all DM events
  6. **
  7. ** Returns void
  8. **
  9. *******************************************************************************/
  10.  
  11. void bte_dm_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC *p_data)
  12. {
  13. /* switch context to btif task context (copy full union size for convenience) */
  14. bt_status_t status = btif_transfer_context(btif_dm_upstreams_evt, (uint16_t)event,
  15. (void*)p_data, sizeof(tBTA_DM_SEC), btif_dm_data_copy);//将函数btif_dm_upstreams_evt transfer 到btif线程执行,也即是向btif线程发送消息
  16. /* catch any failed context transfers */
  17. ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
  18. }

那实际处理的函数就是btif_dm_upstreams_evt ,其参数是BTA_DM_HW_ERROR_EVT

  1. static void btif_dm_upstreams_evt(UINT16 event, char* p_param){
  2. ...
  3. case BTA_DM_HW_ERROR_EVT:
  4. BTIF_TRACE_ERROR("Received H/W Error. ");
  5. /* Flush storage data */
  6. btif_config_flush();
  7. usleep(); /* 100milliseconds */
  8. /* Killing the process to force a restart as part of fault tolerance */
  9. kill(getpid(), SIGKILL);
  10. break;
  11. ...
  12. }

这个函数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. Python+Selenium笔记(一):环境配置+简单的例子

    #环境配置基于windows操作系统 #学习selenium要有一些HTML和xpth的基础,完全不会的建议先花点时间学点基础(不然元素定位,特别是xpth可能看的有点懵) #HTML :  http ...

  2. 控制台中寄宿WCF服务

    一.首先创建一个类库,用来定义WCF服务 修改服务代码定义,具体代码如下 // 注意: 使用"重构"菜单上的"重命名"命令,可以同时更改代码和配置文件中的接口名 ...

  3. LeetCode 题解之 Positions of Large Groups

    1.题目描述 2.问题分析 从头遍历字符串,使用一个局部迭代器和局部变量记录该字符个数.如果个数>= 3 ,则将此时的迭代器位置和局部迭代器的位置保存到局部vector中.再将这个局部vecto ...

  4. .net core项目初建

    电脑装Visual Studio2017,并升级版本.启动一个.net core 的项目. NET Core基本介绍 1.1 什么是ASP.NET Core ASP.NET Core 是一个全新的开源 ...

  5. ndk的注意事项

    从开源网站下载的源码,需要自己编译c源码成so类库.当时用Android studio 运行总是报错"finished with non-zero exit value 2"报错定 ...

  6. Python中的分组函数(groupby、itertools)

    from operator import itemgetter #itemgetter用来去dict中的key,省去了使用lambda函数 from itertools import groupby ...

  7. SQL Server:INFORMATION_SCHEMA.columns 与sys.columns 与 syscolumns对比

    sys.columns视图 sys.columns是SQL Server从2005版本起引入的新的系统级视图.相关链接如下: Mapping SQL Server 2000 System Tables ...

  8. 百度地图POI数据爬取,突破百度地图API爬取数目“400条“的限制11。

    1.POI爬取方法说明 1.1AK申请 登录百度账号,在百度地图开发者平台的API控制台申请一个服务端的ak,主要用到的是Place API.检校方式可设置成IP白名单,IP直接设置成了0.0.0.0 ...

  9. 命令行翻译 推荐一个linux系统中可用的终端小程序

    程序的github地址:https://github.com/fanbrightup/fanyi 使用起来非常简单,同时支持中英文互译甚至是整句. 步骤一:首先你需要安装node,参见我的node安装 ...

  10. ethjs-1-了解

    https://github.com/ethjs/ethjs/blob/master/docs/user-guide.md Install npm install --save ethjs Usage ...