传统蓝牙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)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wget:Unable to estab
- 下一篇: java不使用科学计数法_java不用科