传统蓝牙HCI搜索流程介绍(bluetooth inquiry)
一. 聲明
本專欄文章我們會(huì)以連載的方式持續(xù)更新,本專欄計(jì)劃更新內(nèi)容如下:
第一篇:藍(lán)牙綜合介紹 ,主要介紹藍(lán)牙的一些概念,產(chǎn)生背景,發(fā)展軌跡,市面藍(lán)牙介紹,以及藍(lán)牙開發(fā)板介紹。
第二篇:Transport層介紹,主要介紹藍(lán)牙協(xié)議棧跟藍(lán)牙芯片之前的硬件傳輸協(xié)議,比如基于UART的H4,H5,BCSP,基于USB的H2等
第三篇:傳統(tǒng)藍(lán)牙controller介紹,主要介紹傳統(tǒng)藍(lán)牙芯片的介紹,包括射頻層(RF),基帶層(baseband),鏈路管理層(LMP)等
第四篇:傳統(tǒng)藍(lán)牙host介紹,主要介紹傳統(tǒng)藍(lán)牙的協(xié)議棧,比如HCI,L2CAP,SDP,RFCOMM,HFP,SPP,HID,AVDTP,AVCTP,A2DP,AVRCP,OBEX,PBAP,MAP等等一系列的協(xié)議吧。
第五篇:低功耗藍(lán)牙controller介紹,主要介紹低功耗藍(lán)牙芯片,包括物理層(PHY),鏈路層(LL)
第六篇:低功耗藍(lán)牙host介紹,低功耗藍(lán)牙協(xié)議棧的介紹,包括HCI,L2CAP,ATT,GATT,SM等
第七篇:藍(lán)牙芯片介紹,主要介紹一些藍(lán)牙芯片的初始化流程,基于HCI vendor command的擴(kuò)展
第八篇:附錄,主要介紹以上常用名詞的介紹以及一些特殊流程的介紹等。
另外,開發(fā)板如下所示,對于想學(xué)習(xí)藍(lán)牙協(xié)議棧的最好人手一套。以便更好的學(xué)習(xí)藍(lán)牙協(xié)議棧,相信我,學(xué)完這一套視頻你將擁有修改任何協(xié)議棧的能力(比如Linux下的bluez,Android下的bluedroid)。
------------------------------------------------------------------------------------------------------------------------------------------
CSDN學(xué)院鏈接(進(jìn)入選擇你想要學(xué)習(xí)的課程):https://edu.csdn.net/lecturer/5352?spm=1002.2001.3001.4144
藍(lán)牙交流扣扣群:970324688
Github代碼:https://github.com/sj15712795029/bluetooth_stack
入手開發(fā)板:https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4004-22329603896.18.5aeb41f973iStr&id=622836061708
------------------------------------------------------------------------------------------------------------------------------------------
二.搜索command以及產(chǎn)生的event
藍(lán)牙搜索沒什么好說的吧,就是搜索周邊設(shè)備,也叫inquiry,搜索command以及產(chǎn)生的event如下:
整個(gè)流程如下:
1)首先協(xié)議棧給藍(lán)牙芯片發(fā)送搜索command
2)芯片收到搜索命令上報(bào)協(xié)議棧一個(gè)command status的event
3)然后芯片上報(bào)協(xié)議棧搜索結(jié)果
4)最終芯片上報(bào)協(xié)議棧搜索完成
我們就以上4個(gè)步驟來一一分析下每個(gè)封包,既鞏固我們前面所說的封包格式,又學(xué)到協(xié)議棧跟芯片的交互流程。
首先我們來說下搜索command,命令格式如下:
參數(shù):
LAP:
其中LAP的有效值在:https://www.bluetooth.com/specifications/assigned-numbers/baseband/
Inquiry_Length:搜索時(shí)間
Num_Responses:最多返回的設(shè)備個(gè)數(shù)
注意此部分如果Inquiry_Length跟Num_Responses有一方滿足,那么就停止搜索。
搜索命令具體封包如下:
搜索代碼實(shí)現(xiàn)如下:
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,封包格式如下
參數(shù):
Status:在9.6小節(jié)我們以及介紹errcode的值分別代表什么意思
Num_HCI_Command_Packets:
Command_Opcode:用于標(biāo)示command status是返回哪個(gè)cmmand的,此部分應(yīng)該跟我們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;然后,我們來看下搜索結(jié)果的event,封包格式如下
參數(shù):
Num_Responses:搜索到設(shè)備的個(gè)數(shù),一般芯片會(huì)每次上來一個(gè),然后以多個(gè)搜索結(jié)果event上來,但是也不排除有一次上來多個(gè)搜索結(jié)果的event.
BD_ADDR:搜索到的藍(lán)牙地址.
Page_Scan_Repetition_Mode
Reserved:保留參數(shù)
Class_Of_Device:設(shè)備類型,具體參照:https://www.bluetooth.com/specifications/assigned-numbers/baseband/
Clock_Offset:時(shí)鐘偏移
注意此部分普通的搜索不會(huì)上來remote bluetooth name,需要額外去調(diào)用Remote Name Request command去請求,當(dāng)然EIR除外,后續(xù)我們會(huì)說明EIR。
普通搜索的結(jié)果封包格式如下:
代碼處理如下:
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); /*---通過這個(gè)函數(shù)回調(diào)到APP應(yīng)用層 */}else{BT_HCI_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_memp_malloc fail\n",__FILE__,__FUNCTION__,__LINE__);}}break;最后我們來看下搜索完成的event,封包格式如下:
參數(shù):
Status:搜索完成狀態(tài)
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);/* 通過這個(gè)函數(shù)回調(diào)到APP應(yīng)用層 */break;三.取消搜索command以及產(chǎn)生的event
正常取消搜索流程如下:
這里要重點(diǎn)提一下,個(gè)人覺得不合理的地方,我覺得取消搜索,也應(yīng)該來搜索完成的event,這樣設(shè)計(jì)才合理,我手里的芯片是CSR8811,沒有上報(bào)協(xié)議棧搜索完成的消息,我們暫時(shí)先以這個(gè)流程說明,后續(xù)有了其他芯片重點(diǎn)驗(yàn)證下這個(gè)case
回到主題,流程如下:
1)發(fā)送取消搜索的命令
2)收到command complete with inquiry cancel opcode
我們來整個(gè)分析下這兩個(gè)步驟
步驟1:發(fā)送取消搜索命令,取消搜索的命令格式如下,很簡單的一個(gè)命令,甚至連參數(shù)都沒有
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的封包格式如下:
參數(shù):
Num_HCI_Command_Packets:
Command_Opcode:這個(gè)opcode就不用介紹了吧,很熟悉啊·
Return_Parameter(s):返回的參數(shù),要看具體的命令返回什么參數(shù),我們在取消搜索的時(shí)候看到返回status的參數(shù),所以此部分應(yīng)該是status。
直接上Wireshark嘍
在代碼中我們在event input中監(jiān)測command complete處理如下
另外,我送個(gè)彩蛋,就是我比較好奇他說如果BE/EDR處于搜索狀態(tài),那么就取消搜索,但是如果此時(shí)我們不在搜索中我們下取消搜索會(huì)怎么樣呢,以下是我試的結(jié)果,貼上來下:
我們會(huì)同樣受到command complete,但是status結(jié)果是command disallowed.
總結(jié)
以上是生活随笔為你收集整理的传统蓝牙HCI搜索流程介绍(bluetooth inquiry)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wget:Unable to estab
- 下一篇: java不使用科学计数法_java不用科