本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:
第一篇:蓝牙综合介绍 ,主要介绍蓝牙的一些概念,产生背景,发展轨迹,市面蓝牙介绍,以及蓝牙开发板介绍。
第二篇: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
蓝牙学习目录:https://blog.csdn.net/XiaoXiaoPengBo/article/details/107727900
------------------------------------------------------------------------------------------------------------------------------------------
此部分指的是hci的连接,不是上层profile的连接,流程如下:
总结步骤:
1)蓝牙协议栈向芯片发送连接的command
2)蓝牙芯片上报蓝牙协议栈command status with create connection opcode
3)蓝牙芯片上报蓝牙协议栈connect complete
下面我们就来一一分析3个步骤
步骤1):蓝牙协议栈像芯片发送连接的command,连接command封包格式如下:
参数:
BD_ADDR:要连接的remote设备的蓝牙地址
Packet_Type:支持的数据封包类型。
Page_Scan_Repetition_Mode:page scan重复模式
Reserved: 保留
Clock_Offset:时钟偏移。
Allow_Role_Switch:是否允许角色转换
我们直接来看下wireshark转的snoop
代码如下:
err_t hci_connect_req(struct bd_addr_t *bdaddr, uint8_t allow_role_switch)
{
uint8_t page_scan_repetition_mode, page_scan_mode;
uint16_t clock_offset;
struct bt_pbuf_t *p;
struct hci_link_t *link = hci_new();
struct hci_inq_res_t *inqres;
if(link == NULL)
{
BT_HCI_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] fail\n",__FILE__,__FUNCTION__,__LINE__);
return BT_ERR_MEM; /* Could not allocate memory for link */
}
link->state = SEND_CREATE_CONNECTION;
bd_addr_set(&(link->bdaddr), bdaddr);
HCI_REG(&(hci_active_links), link);
/* Check if module has been discovered in a recent inquiry */
for(inqres = pcb->ires; inqres != NULL; inqres = inqres->next)
{
if(bd_addr_cmp(&inqres->bdaddr, bdaddr))
{
page_scan_repetition_mode = inqres->psrm;
page_scan_mode = inqres->psm;
clock_offset = inqres->co;
break;
}
}
if(inqres == NULL)
{
/* No information on parameters from an inquiry. Using default values */
page_scan_repetition_mode = 0x01; /* Assuming worst case: time between
successive page scans starting
<= 2.56s */
page_scan_mode = 0x00; /* Assumes the device uses mandatory scanning, most
devices use this. If no conn is established, try
again w this parm set to optional page scanning */
clock_offset = 0x00; /* If the device was not found in a recent inquiry
this information is irrelevant */
}
if((p = bt_pbuf_alloc(BT_TRANSPORT_TYPE, HCI_CREATE_CONN_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_CREATE_CONNECTION, HCI_LINK_CONTROL, HCI_CREATE_CONN_PLEN);
/* Assembling cmd prameters */
memcpy(((uint8_t *)p->payload)+3, bdaddr->addr, 6);
bt_le_store_16((uint8_t *)p->payload,9,HCI_PACKET_TYPE);
((uint8_t *)p->payload)[11] = page_scan_repetition_mode;
((uint8_t *)p->payload)[12] = page_scan_mode;
bt_le_store_16((uint8_t *)p->payload,13,clock_offset);
((uint8_t *)p->payload)[15] = allow_role_switch;
phybusif_output(p, p->tot_len,PHYBUSIF_PACKET_TYPE_CMD);
bt_pbuf_free(p);
return BT_ERR_OK;
}
步骤2)芯片上报给蓝牙协议栈的command status with create connetion opcode
由于前面我们已经讲解了command status的event格式,所以我们在这就不再贴了,直接上wireshark抓的btsnoop
Event command status的代码处理也贴过了,我们直接来看步骤3
步骤3)蓝牙芯片向蓝牙协议栈上报connection complete事件,封包格式如下
参数:
Status:连接的状态
Connection_Handle:连接的句柄
BD_ADDR:连接的蓝牙地址
Link_Type:连接的类型
Encryption_Enabled: 是否允许加密
下面我们来直接看下Wireshark抓的btsnoop加深下印象
最后,我们来看看代码中是怎么处理的
case HCI_CONNECTION_COMPLETE:
bdaddr = (void *)(((uint8_t *)p->payload)+3); /* Get the Bluetooth address */
link = hci_get_link(bdaddr);
switch(((uint8_t *)p->payload)[0])
{
case HCI_SUCCESS:
BT_HCI_TRACE_DEBUG("hci_event_input: Conn successfully completed\n");
if(link == NULL)
{
if((link = hci_new()) == NULL)
{
/* Could not allocate memory for link. Disconnect */
BT_HCI_TRACE_DEBUG("hci_event_input: Could not allocate memory for link. Disconnect\n");
hci_disconnect_acl(bdaddr, HCI_OTHER_END_TERMINATED_CONN_LOW_RESOURCES);
/* Notify L2CAP */
lp_disconnect_ind(bdaddr);
break;
}
bd_addr_set(&(link->bdaddr), bdaddr);
link->conhdl = *((uint16_t *)(((uint8_t *)p->payload)+1));
HCI_REG(&(hci_active_links), link);
HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret); /* Allow applicaton to do optional configuration of link */
BT_HCI_TRACE_DEBUG("hci_event_input: Calling l2cap cb 1\n");
hci_get_remote_feature(bdaddr);
if(link->state == SEND_CREATE_CONNECTION)
{
lp_connect_cfm(bdaddr, ((uint8_t *)p->payload)[10], BT_ERR_OK); /* Notify L2CAP */
}
else
lp_connect_ind(&(link->bdaddr)); /* Notify L2CAP */
link->state = OPEN;
}
else
{
link->conhdl = *((uint16_t *)(((uint8_t *)p->payload)+1));
HCI_EVENT_CONN_COMPLETE(pcb,bdaddr,ret); /* Allow applicaton to do optional configuration of link */
BT_HCI_TRACE_DEBUG("hci_event_input: Calling l2cap cb 2\n");
hci_get_remote_feature(bdaddr);
if(link->state == SEND_CREATE_CONNECTION)
lp_connect_cfm(bdaddr, ((uint8_t *)p->payload)[10], BT_ERR_OK); /* Notify L2CAP */
else
lp_connect_ind(&(link->bdaddr)); /* Notify L2CAP */
link->state = OPEN;
}
//TODO: MASTER SLAVE SWITCH??
break;
老规矩,我们先来看下Wireshark整个封包的交互流程
步骤整理如下:
1)收到芯片上报给协议栈connect request的事件
2)协议栈发送给芯片接受连接请求的command
3)收到command status with accept connection req的opcode
4)收到connect complete的event
步骤1)的connection request的event的封包格式如下
参数:
BD_ADDR:蓝牙地址
Class_of_Device: 对方的cod
Link_Type:连线类型
老规矩直接来看下Wireshark抓的btsnoop
然后看下代码我们是怎么处理的
case HCI_CONNECTION_REQUEST:
bdaddr = (void *)(((uint8_t *)p->payload)); /* Get the Bluetooth address */
link_type = ((uint8_t *)p->payload)[9];
BT_HCI_TRACE_DEBUG("hci_event_input: recv conn req type %d\n",link_type);
link = hci_get_link(bdaddr);
if(link == NULL)
{
if((link = hci_new()) == NULL)
{
link->state = RECEIVED_CONNECTION_REQUEST;
/* Could not allocate memory for link. Disconnect */
BT_HCI_TRACE_DEBUG("hci_event_input: Could not allocate memory for link. reject\n");
hci_reject_connection_request(bdaddr,HCI_HOST_REJECTED_DUE_TO_LIMITED_RESOURCES);
link->state = REJECTED_CONNECTION_REQUEST;
}
else
{
bd_addr_set(&(link->bdaddr), bdaddr);
/* TODO 此部分是否需要反转 */
memcpy(&link->cod,((uint8_t *)p->payload)+6,3);
link->role = HCI_ROLE_SLAVE;
BT_HCI_TRACE_DEBUG("hci_event_input: con req cod 0x%x\n",link->cod);
HCI_REG(&(hci_active_links), link);
hci_accept_connection_request(bdaddr,HCI_ROLE_SLAVE);
link->state = ACCEPTED_CONNECTION_REQUEST;
}
}
else
{
if(link_type == HCI_LINK_TYPE_ACL)
{
/* Could not allocate memory for link. Disconnect */
BT_HCI_TRACE_DEBUG("hci_event_input: con exit. reject\n");
hci_reject_connection_request(bdaddr,HCI_ACL_CONNECTION_EXISTS);
link->state = REJECTED_CONNECTION_REQUEST;
hci_close(link);
}
else
{
HCI_EVENT_SCO_REQ(pcb, bdaddr, ret);
}
}
break;
步骤2)蓝牙协议栈发送给蓝牙芯片接受连接请求的命令封包格式如下:
参数:
BD_ADDR:蓝牙地址
Role:当前连接的角色
我们来看下Wireshark的抓的snoop
代码如下:
err_t hci_accept_connection_request(struct bd_addr_t *bdaddr, uint8_t role)
{
struct bt_pbuf_t *p;
if((p = bt_pbuf_alloc(BT_TRANSPORT_TYPE, HCI_ACCEPT_CON_REQ_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;
}
/* Assembling command packet */
p = hci_cmd_ass(p, HCI_ACCEPT_CONNECTION_REQUEST, HCI_LINK_CONTROL, HCI_ACCEPT_CON_REQ_PLEN);
/* Assembling cmd prameters */
memcpy(((uint8_t *)p->payload) + 3, bdaddr->addr, 6);
((uint8_t *)p->payload)[9] = role;
phybusif_output(p, p->tot_len,PHYBUSIF_PACKET_TYPE_CMD);
bt_pbuf_free(p);
return BT_ERR_OK;
}
步骤3)收到command status with accept connection req的opcode
由于command status的格式以及代码我们已经在前面介绍,所以此部分我们只贴BTSNOOP
步骤4)收到connect complete的event
跟主动连接一样,就不在这里重复说明
这个小节其实还有很多要讲的,以上我们上面讲的主要是OGF=1的小部分命令以及event
我们在这个小节就不讲其他OGF了,我们在下一个小节,蓝牙初始化的时候来说明其他OGF,初始化主要会牵扯都OGF=3/OGF=4的部分command