Wednesday, 12 August 2020 10:05

蓝牙协议栈学习/开发利器-BTSNOOP介绍 Featured

Btsnoop用于记录蓝牙协议栈跟芯片交互的数据,一般用于在分析蓝牙问题的时候有很大的用途,能够快速定位问题所在,当然对于学习蓝牙协议栈也有很大的用途,我们后续的教程也是基于btsnoop来分析协议。

在android手机中也有开启btsnoop的功能,在开发者选项中勾中这个就开启了btsnoop功能。

一. 声明


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

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

第二篇: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学院链接(进入选择你想要学习的课程):undefined

蓝牙交流扣扣群:970324688

Github代码:undefined

入手开发板:undefined

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

二. 前言


首先在介绍以下内容之前,我们先来介绍下我们的CSDN课程,以下介绍内容都会在 CSDN课程 蓝牙协议栈传输介质Transport介绍以及btsnoop的应用(点击我)中第三小节/第四小节介绍。

三.Btsnoop利器介绍

1. Btsnoop概念介绍

开发过Android 蓝牙的人肯定知道Btsnoop,但是我还是罗嗦一下概念,让那些不懂Btsnoop的人看下。

Btsnoop用于记录蓝牙协议栈跟芯片交互的数据,一般用于在分析蓝牙问题的时候有很大的用途,能够快速定位问题所在,当然对于学习蓝牙协议栈也有很大的用途,我们后续的教程也是基于btsnoop来分析协议。

在android手机中也有开启btsnoop的功能,在开发者选项中勾中这个就开启了btsnoop功能。注意:每个手机生成的btsnoop的路径不同,所以要仔细找下,更为坑爹的是:有的手机升级后btsnoop竟然功能失效,比如我的手机,从Android 8升级到Android 9后竟然btsnoop的功能失效了,坑不坑。但是没有关系哈,我们在我们的开发板代码中也实现了btsnoop功能。手机开启btsnoop的功能的地方如下图:

Btsnoop有多种格式,分别有简化版的(比如btstack source code就是实现了简化版的),简化版的只能在Wireshark中打开,而我们实现的可以在Wireshark,Frontline跟Ellisys都可以打开,我个人觉得实现我们这种方式好,因为牵扯到工具的使用,Wireshark虽然对于分析tcp/ip协议不错,但是没有协议汇总功能。我们来看下btsnoop在各个工具中打开的效果图:

1)Wireshark打开效果图

2)Frontline打开效果图

3)Ellisys打开效果图

关于这3个工具的使用,我们会单独一篇文章来大概说明,我个人比较喜欢Ellisys的工具来看

2. Btsnoop格式介绍

此部分是直接看frontline官网整理的btsnoop格式

undefined

这个格式如下(所有的数据都是大端模式,也就是高位在前)

下面我们来分别介绍下文件格式的几部分内容

1)File Header

文件头是一个固定长度的字段,包含关于包文件及其包含的包记录的格式的一般信息。文件头字段后面有一个或多个可变长度的包记录字段。

整个文件头包括Identification Pattern(8 byte),Version Number(4 Byte),Datalink Type(4 Byte).

① Identification Pattern

就是特定的十六进制数据,以NULL结尾0x62 0x74 0x73 0x6e 0x6f 0x6f 0x70 0x0,Asciii的值为”btsnoop”

② Version Number,暂时一直为1

③ Datalink Type,类型定义如下:

此部分android的bluedroid不管是H5还是BCSP都会剥开transport数据,来以H4方式存储,这样足以可以看到蓝牙交互流程了。

下面我们随便打开一个我之前录制好的btsnoop来解析下file header。

其中data link是0x3ea=1002是H4,所以看这部分完全符合btsnoop的file header部分

2) Packet recored,此部分就是蓝牙协议栈跟蓝牙芯片每包交互的数据,格式如下

Packet recored包括Original Length,Included Length,Packet Flags,Cumulative Drops,Timestamp Microseconds,Packet Data.

因为此部分我们以H4来讨论,所以就直接按照H4来写

① Original Lenth:4 byte ,此部分说白了就是H4 packet的数据长度

② Included Length:等于Original Lenth

③ Packet Flag:包类型

hci cmd是协议栈发送数据给蓝牙芯片,所以bit 0=0,bit1=1也就是0x02

hci event是协议栈从蓝牙芯片接收数据,所以bit 0=1,bit1=1也就是0x03

hci acl out是协议栈发送数据给蓝牙芯片,所以bit 0=0,bit1=0也就是0x00

hci acl in是协议栈从蓝牙芯片接收数据,所以bit 0=1,bit1=0也就是0x01

hci sco out是协议栈发送数据给蓝牙芯片,所以bit 0=0,bit1=0也就是0x00

hci sco in是协议栈从蓝牙芯片接收数据,所以bit 0=1,bit1=0也就是0x01

④ Cumulative Drops:计算丢包功能,此部分我们设置为0

⑤ Timestamp Microseconds:毫秒,此部分是是从公元0年1月1号午夜开始算起,

所以此部分如果要在android跟Linux,unix用的话,那么需要加上1970年,因为1970年是unix元年,也就是诞生的日子,所以会看到android部分会在时间上加上这部分0x00dcddb30f2f8000ULL,这部分我还真算过,时间是这么久。

1971年多点·,但是此部分我很诧异,觉得应该是刚好1971年的,所以如果有懂的可以提一下,让我也涨涨见识。

⑥ Pakcet data:就是transport数据了,此部分我们是H4,所以就是H4数据

3. Btsnoop实现方式介绍

因为Btsnoop是写文件的方式,对于有文件系统的开发板来说可以直接用,比如Linux可以直接open/write,有fatfs/littilfs或者等其他file system的板子来说都好办,都可以直接存储,主要考虑下存储空间就好了,但是你有想过没,对于没有文件系统的开发板呢?难道除了买air sniffer来说就没有别的办法了吗?况且蓝牙空中抓包器那么贵(我是指传统蓝牙的抓包哈,BLE的抓包可能只是几十块钱),那我们有什么办法呢?有,我在其他开发板中实现过,现在我来说说当初我的思路。

对于没有文件系统的开发板实现方式转换一层思路就好了,通过debug方式把HCI数据交于PC处理,就跟打调试日志一样的道理,只不过换成了HCI数据,但是需要自己实现上位机,来解析HCI数据,以及在PC端按照2小节的格式来存储起来就好了,架构如下:

4. Btsnoop代码介绍

代码以Linux为例哈,我们分别会讲btsnoop初始化/存储btsnoop/btsnoop关闭

1)btsnoop初始化代码如下:

err_t bt_snoop_init(void)
{
time_t t;
struct tm *p;

time(&t);
p = gmtime(&t);
uint8_t bt_snoop_name[64] = {0};

sprintf(bt_snoop_name,"%sbtsnoop_%04d_%02d_%02d_%02d_%02d_%02d.log",BT_SNOOP_PATH,1900 + p->tm_year,1 + p->tm_mon, \
p->tm_mday,8 + p->tm_hour,p->tm_min,p->tm_sec);/* time zone hour+8 */


bt_snoop_file = open(bt_snoop_name, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(bt_snoop_file 0)
{
BT_SNOOP_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_snoop_init open fail\n",__FILE__,__FUNCTION__,__LINE__);
return BT_ERR_VAL;
}
BT_SNOOP_TRACE_DEBUG("bt snoop fd %d\n",bt_snoop_file);

write(bt_snoop_file, "btsnoop\0\0\0\0\1\0\0\x3\xea", 16);

return BT_ERR_OK;
}

2)btsnoop写record data

/******************************************************************************
* func name : bt_snoop_write
* para : packet_type(IN) --> packet type
in(IN) --> 标示是否是蓝牙芯片到协议栈的数据
packet(IN) --> 要写的的HCI数据
len(IN) --> 要写入的长度
* return : 返回bt_snoop_write的结果
* description : 把HCI数据写入到btsnoop file中
******************************************************************************/
err_t bt_snoop_write(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len)
{
struct timeval curr_time;
struct tm* ptm;
uint32_t length_he = 0;
uint32_t flags = 0;
uint64_t time_stamp = 0;
btsnoop_header_t header;
if (bt_snoop_file 0)
{
BT_SNOOP_TRACE_ERROR("ERROR:file[%s],function[%s],line[%d] bt_snoop_write fd invalid\n",__FILE__,__FUNCTION__,__LINE__);
return BT_ERR_VAL;
}
//BT_SNOOP_TRACE_DEBUG("bt_snoop_write type(%d) in(%d) len(%d)\n",packet_type,in,len);
gettimeofday(&curr_time, NULL);
time_stamp = (curr_time.tv_sec+8*60*60)*1000000ULL+curr_time.tv_usec+\
BTSNOOP_EPOCH_DELTA;/* time zone hour+8 */
switch (packet_type)
{
case BT_SNOOP_PACKET_TYPE_CMD:
length_he = packet[2] + 4;
flags = 2;
break;
case BT_SNOOP_PACKET_TYPE_ACL_DATA:
length_he = (packet[3] 8) + packet[2] + 5;
flags = in;
break;
case BT_SNOOP_PACKET_TYPE_SCO_DATA:
length_he = packet[2] + 4;
flags = in;
break;
case BT_SNOOP_PACKET_TYPE_EVT:
length_he = packet[1] + 3;
flags = 3;
break;
default:
return;
}
header.length_original = bt_htonl(length_he);
header.length_captured = header.length_original;
header.flags = bt_htonl(flags);
header.dropped_packets = 0;
header.timestamp = bt_htonll(time_stamp);
header.type = packet_type;
write(bt_snoop_file,&header, sizeof(btsnoop_header_t));
write(bt_snoop_file,packet, length_he - 1);
return BT_ERR_OK;
}

3)btsnoop关闭

/******************************************************************************
* func name : bt_snoop_deinit
* para : VOID


* return : 返回bt_snoop_deinit结果
* description : btsnoop的deinit
******************************************************************************/
err_t bt_snoop_deinit(void)
{
if(bt_snoop_file != -1)
close(bt_snoop_file);
return BT_ERR_OK;
}

Read 21268 times Last modified on Wednesday, 12 August 2020 11:14

GPS singal acquisition,replay and test equipment

Portable singal acquisition and replay

AI intelligent tongue imager

Tongue imager rafavi
 
Please support our site by viewing this advertisement.

Please support our site by viewing this advertisement

Free Content