欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

传统蓝牙HCI搜索流程介绍(bluetooth inquiry)

发布时间:2023/12/16 33 豆豆
生活随笔 收集整理的这篇文章主要介绍了 传统蓝牙HCI搜索流程介绍(bluetooth inquiry) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

一. 声明

本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:

第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。

第二篇:Transport层介绍,主要介绍蓝牙协议栈跟蓝牙芯片之前的硬件传输协议,比如基于UART的H4,H5,BCSP,基于USB的H2等

第三篇:传统蓝牙controller介绍,主要介绍传统蓝牙芯片的介绍,包括射频层(RF),基带层(baseband),链路管理层(LMP)等

第四篇:传统蓝牙host介绍,主要介绍传统蓝牙的协议栈,比如HCI,L2CAP,SDP,RFCOMM,HFP,SPP,HID,AVDTP,AVCTP,A2DP,AVRCP,OBEX,PBAP,MAP等等一系列的协议吧。

第五篇:低功耗蓝牙controller介绍,主要介绍低功耗蓝牙芯片,包括物理层(PHY),链路层(LL)

第六篇:低功耗蓝牙host介绍,低功耗蓝牙协议栈的介绍,包括HCI,L2CAP,ATT,GATT,SM等

第七篇:蓝牙芯片介绍,主要介绍一些蓝牙芯片的初始化流程,基于HCI vendor command的扩展

第八篇:附录,主要介绍以上常用名词的介绍以及一些特殊流程的介绍等。

另外,开发板如下所示,对于想学习蓝牙协议栈的最好人手一套。以便更好的学习蓝牙协议栈,相信我,学完这一套视频你将拥有修改任何协议栈的能力(比如Linux下的bluez,Android下的bluedroid)。

------------------------------------------------------------------------------------------------------------------------------------------

CSDN学院链接(进入选择你想要学习的课程):https://edu.csdn.net/lecturer/5352?spm=1002.2001.3001.4144

蓝牙交流扣扣群:970324688

Github代码:https://github.com/sj15712795029/bluetooth_stack

入手开发板:https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22329603896.18.5aeb41f973iStr&id=622836061708

------------------------------------------------------------------------------------------------------------------------------------------

二.搜索command以及产生的event

蓝牙搜索没什么好说的吧,就是搜索周边设备,也叫inquiry,搜索command以及产生的event如下:

整个流程如下:

1)首先协议栈给蓝牙芯片发送搜索command

2)芯片收到搜索命令上报协议栈一个command status的event

3)然后芯片上报协议栈搜索结果

4)最终芯片上报协议栈搜索完成

我们就以上4个步骤来一一分析下每个封包,既巩固我们前面所说的封包格式,又学到协议栈跟芯片的交互流程。

首先我们来说下搜索command,命令格式如下:

参数:

LAP:

其中LAP的有效值在:https://www.bluetooth.com/specifications/assigned-numbers/baseband/

Inquiry_Length:搜索时间

Num_Responses:最多返回的设备个数

注意此部分如果Inquiry_Length跟Num_Responses有一方满足,那么就停止搜索。

搜索命令具体封包如下:

搜索代码实现如下:

err_t hci_inquiry(uint32_t lap, uint8_t inq_len, uint8_t num_resp,err_t (*inq_result)(struct hci_pcb_t *pcb,struct hci_inq_res_t *inqres),err_t (* inq_complete)(struct hci_pcb_t *pcb,uint16_t result)) {struct bt_pbuf_t *p;struct hci_inq_res_t *tmpres;/* Free any previous inquiry result list */while(pcb->ires != NULL){tmpres = pcb->ires;pcb->ires = pcb->ires->next;bt_memp_free(MEMP_HCI_INQ, tmpres);}pcb->inq_complete = inq_complete;pcb->inq_result = inq_result;if((p = bt_pbuf_alloc(BT_TRANSPORT_TYPE, HCI_INQUIRY_PLEN, BT_PBUF_RAM)) == NULL){BT_HCI_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_pbuf_alloc fail\n",__FILE__,__FUNCTION__,__LINE__);return BT_ERR_MEM; /* Could not allocate memory for bt_pbuf_t */}/* Assembling command packet */p = hci_cmd_ass(p, HCI_INQUIRY, HCI_LINK_CONTROL, HCI_INQUIRY_PLEN);/* Assembling cmd prameters */bt_le_store_24((uint8_t *)p->payload,3,lap);((uint8_t *)p->payload)[6] = inq_len;((uint8_t *)p->payload)[7] = num_resp;phybusif_output(p, p->tot_len,PHYBUSIF_PACKET_TYPE_CMD);bt_pbuf_free(p);return BT_ERR_OK; }

其次,我们来看下command status的event,封包格式如下

参数:

Status:在9.6小节我们以及介绍errcode的值分别代表什么意思

Num_HCI_Command_Packets:

Command_Opcode:用于标示command status是返回哪个cmmand的,此部分应该跟我们HCI command的opcode一样,此部分也就是inquiry command.

Command status封包格式如下:

代码中没有针对此部分做特别处理,基本上算是打印:

case HCI_COMMAND_STATUS:switch(((uint8_t *)p->payload)[0]){case HCI_SUCCESS:BT_HCI_TRACE_DEBUG("hci_event_input: Command Status\n");break;default:BT_HCI_TRACE_DEBUG("hci_event_input: Command failed, %s\n", hci_get_error_code(((uint8_t *)p->payload)[0]));bt_pbuf_header(p, -2); /* Adjust payload pointer not to coverNum_HCI_Command_Packets and status parameter */ocf = *((uint16_t *)p->payload) & 0x03FF;ogf = *((uint16_t *)p->payload) >> 10;bt_pbuf_header(p, -2); /* Adjust payload pointer not to cover Command_Opcodeparameter */HCI_EVENT_CMD_COMPLETE(pcb,ogf,ocf,((uint8_t *)p->payload)[0],ret);bt_pbuf_header(p, 4);break;}BT_HCI_TRACE_DEBUG("Num_HCI_Command_Packets: 0x%x\n", ((uint8_t *)p->payload)[1]);pcb->numcmd += ((uint8_t *)p->payload)[1]; /* Add number of completed command packets to thenumber of command packets that the BT modulecan buffer */BT_HCI_TRACE_DEBUG("Command_Opcode: 0x%x 0x%x\n", ((uint8_t *)p->payload)[2], ((uint8_t *)p->payload)[3]);break;

然后,我们来看下搜索结果的event,封包格式如下

参数:

Num_Responses:搜索到设备的个数,一般芯片会每次上来一个,然后以多个搜索结果event上来,但是也不排除有一次上来多个搜索结果的event.

BD_ADDR:搜索到的蓝牙地址.

Page_Scan_Repetition_Mode

Reserved:保留参数

Class_Of_Device:设备类型,具体参照:https://www.bluetooth.com/specifications/assigned-numbers/baseband/

Clock_Offset:时钟偏移

注意此部分普通的搜索不会上来remote bluetooth name,需要额外去调用Remote Name Request command去请求,当然EIR除外,后续我们会说明EIR。

普通搜索的结果封包格式如下:

代码处理如下:

case HCI_INQUIRY_RESULT:for(i=0; i<((uint8_t *)p->payload)[0]; i++){resp_offset = i*14;BT_HCI_TRACE_DEBUG("hci_event_input: Inquiry result %d\nBD_ADDR: 0x",i);for(i = 0; i < BD_ADDR_LEN; i++){BT_HCI_TRACE_DEBUG("%x",((uint8_t *)p->payload)[1+resp_offset+i]);}BT_HCI_TRACE_DEBUG("\n");BT_HCI_TRACE_DEBUG("Page_Scan_Rep_Mode: 0x%x\n",((uint8_t *)p->payload)[7+resp_offset]);BT_HCI_TRACE_DEBUG("Class_of_Dev: 0x%x 0x%x 0x%x\n",((uint8_t *)p->payload)[10+resp_offset],((uint8_t *)p->payload)[11+resp_offset], ((uint8_t *)p->payload)[12+resp_offset]);BT_HCI_TRACE_DEBUG("Clock_Offset: 0x%x%x\n",((uint8_t *)p->payload)[13+resp_offset],((uint8_t *)p->payload)[14+resp_offset]);bdaddr = (void *)(((uint8_t *)p->payload)+(1+resp_offset));if((inqres = bt_memp_malloc(MEMP_HCI_INQ)) != NULL){bd_addr_set(&(inqres->bdaddr), bdaddr);inqres->psrm = ((uint8_t *)p->payload)[7+resp_offset];inqres->psm = ((uint8_t *)p->payload)[9+resp_offset];memcpy(inqres->cod, ((uint8_t *)p->payload)+10+resp_offset, 3);inqres->co = *((uint16_t *)(((uint8_t *)p->payload)+13+resp_offset));HCI_REG(&(pcb->ires), inqres);HCI_EVENT_INQ_RESULT(pcb,inqres,ret); /*---通过这个函数回调到APP应用层 */}else{BT_HCI_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_memp_malloc fail\n",__FILE__,__FUNCTION__,__LINE__);}}break;

最后我们来看下搜索完成的event,封包格式如下:

参数:

Status:搜索完成状态

Wireshark封包格式如下:

代码处理如下:

case HCI_INQUIRY_COMPLETE:BT_HCI_TRACE_DEBUG("DEBUG:hci_event_input: Inquiry complete, 0x%x %s\n",((uint8_t *)p->payload)[0], hci_get_error_code(((uint8_t *)p->payload)[0]));HCI_EVENT_INQ_COMPLETE(pcb,((uint8_t *)p->payload)[0],ret);/* 通过这个函数回调到APP应用层 */break;

三.取消搜索command以及产生的event

正常取消搜索流程如下:

这里要重点提一下,个人觉得不合理的地方,我觉得取消搜索,也应该来搜索完成的event,这样设计才合理,我手里的芯片是CSR8811,没有上报协议栈搜索完成的消息,我们暂时先以这个流程说明,后续有了其他芯片重点验证下这个case

回到主题,流程如下:

1)发送取消搜索的命令

2)收到command complete with inquiry cancel opcode

我们来整个分析下这两个步骤

步骤1:发送取消搜索命令,取消搜索的命令格式如下,很简单的一个命令,甚至连参数都没有

Wireshark封包格式如下:

代码如下:

err_t hci_cancel_inquiry(void) {struct bt_pbuf_t *p;struct hci_inq_res_t *tmpres;/* Free any previous inquiry result list */while(pcb->ires != NULL){tmpres = pcb->ires;pcb->ires = pcb->ires->next;bt_memp_free(MEMP_HCI_INQ, tmpres);}if((p = bt_pbuf_alloc(BT_TRANSPORT_TYPE, HCI_CANCEL_INQUIRY_PLEN, BT_PBUF_RAM)) == NULL){BT_HCI_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_pbuf_alloc fail\n",__FILE__,__FUNCTION__,__LINE__);return BT_ERR_MEM; /* Could not allocate memory for bt_pbuf_t */}/* Assembling command packet */p = hci_cmd_ass(p, HCI_INQUIRY_CANCEL, HCI_LINK_CONTROL, HCI_CANCEL_INQUIRY_PLEN);phybusif_output(p, p->tot_len,PHYBUSIF_PACKET_TYPE_CMD);bt_pbuf_free(p);return BT_ERR_OK; }

步骤2:收到command complete with inquiry cancel opcode,command complete的封包格式如下:

参数:

Num_HCI_Command_Packets:

Command_Opcode:这个opcode就不用介绍了吧,很熟悉啊·

Return_Parameter(s):返回的参数,要看具体的命令返回什么参数,我们在取消搜索的时候看到返回status的参数,所以此部分应该是status。

直接上Wireshark喽

在代码中我们在event input中监测command complete处理如下

另外,我送个彩蛋,就是我比较好奇他说如果BE/EDR处于搜索状态,那么就取消搜索,但是如果此时我们不在搜索中我们下取消搜索会怎么样呢,以下是我试的结果,贴上来下:

我们会同样受到command complete,但是status结果是command disallowed.

总结

以上是生活随笔为你收集整理的传统蓝牙HCI搜索流程介绍(bluetooth inquiry)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。