在一次测试公司自研的蓝牙协议栈同自己的笔记本电脑(Thinkpad)连接时,发现连接成功后,无法播放音乐,点击电脑右下角的喇叭图标,只有一个Hands-Free AG audio图标,没有A2DP Stereo audio图标,如图:
抓取协议栈hci log进一步分析发现,HFP、AVRCP、AVDTP signaling channel、AVDTP Media Channel均有连上,AVDTP Signaling channel上也有正常的配置交互流程,但是在电脑上播放音乐,电脑不会发送AVDTP_START命令:
继续追AVDTP signaling channel & AVDTP media channel l2cap建立的过程,也没有发现问题:
signaling channel:
media channel:
查看SDP的交互流程,
对端设备有成功获取A2DP Sink Attribute, AVRCP Controller Attribute,AVRCP Target Attribute,HFP Attribute:
到此,所有的流程和配置均没有发现问题,且使用此协议栈同手机连接,可以正常打电话和播音乐。
我有以下几个猜想:
1.是否是我用的这台电脑的蓝牙有问题?
有可能这台电脑蓝牙所使用的协议栈,驱动,或者硬件有问题,虽然概率极低,但是也不能排除,于是我用自己的漫步者蓝牙耳机同电脑连接,发现一切正常,可以打电话、播放音乐。
可以排除这个猜想。
2.是否是本端Controller的问题?
我们公司的Controller chip量产时间不久,测试覆盖面不大,没有和这一类笔记本电脑测试过,在之前的测试中也遇到因为Controller不完全遵循BT Spec导致的一些问题,所以也可以猜想是否是类似的原因。
于是我将bluekitchen btstack 移植到了我们的RTOS SDK中(在移植之前,我用btstack搭配高通csr8311蓝牙模块做了简单的测试,以确保btstack在配置上没有问题),测试发现,btstack搭配我们公司的controller,连接笔记本电脑,依旧没有问题,可以正常的打电话、播音乐。
Controller有问题的猜想也可以排除,由此可以确定是Host的问题。
3.确定是本端Host的问题
我在上一步测试中,抓取了btstack的hci log,通过对比我们协议栈的hci log 和 btstack hci log,尝试从中找出不同的地方,来进一步定位问题点。
以下是不同点:
(1)在建立ACL链接时设置的Role不同
our stack role: master
btstack role: slave
这里不同的Role设置会带来什么影响呢?我也不清楚,只能去查下Core Spec:
HCI_ACCEPT_CONNECTION_REQUEST_CMD:
HCI_SWITCH_ROLE_CMD:
HCI_ROLE_CHANGE_EVT:
HCI_ERR_CODE_ROLE_CHANGE_NOT_ALLOWED:
将role也改为slave,再次测试,还是没有效果,这个差异带来的影响可以排除。
(2)SDP Request Response方式不同,导致Profile建立的顺序不同
在收到对端发来的SDP Search/Attribute Request后,our stack每次只回复1-2个SDP attribute,然后对端继续发送SDP Search/Attribute Request,直到获取所有的SDP attribute,而btstack一次性回复所有的SDP attribute。
经过仔细对比,发现在建立SDP l2cap channel时,l2cap configuration流程中,our stack给SDP l2cap channel配置的mtu size为128,而btstack配置的mtu size为1024。
our stack:
btstack:
将SDP MTU改为1024后,再次测试,SDP交互流程已经和btstack相同,且profile建立顺序也一样,但依然没有效果,这个差异带来的影响可以排除。
(3)AVRCP交互流程不同
our stack:
btstack:
查看代码发现,our stack中AVRCP配置为CT角色,而btstack中AVRCP配置为CT & TG双角色,想要快速定位是否是AVRCP带来的问题,直接关闭AVRCP feature就好。
关闭AVRCP feature后,再次测试,奇迹出现了,A2DP stereo出现了,可以正常播放音乐,打电话。
到此,终于定位到了问题来源是AVRCP,经过仔细查阅AVRCP SPEC,发现并没有强制要求设置为双角色,只要行为和角色定义一致就行。并且我将AVRCP role和交互流程改为和btstack相同之后,问题仍未解决。
最终经过仔细对比our stack & btstack AVRCP hci log,发现our stack在回复电脑发来的AVRCP Get_Capabilities时,l2cap layer多发送了4个字节0x00。
最后查看代码,发现是在填写avrcp_send_get_cap_resp的buffer length时,多给了4个byte,将buffer length纠正之后,问题就解决了。
(这里不得不吐槽下这段祖传代码,这么重要的length参数居然写的hard-code,可见代码规范是多么的重要啊!)
bt_status_t avrcp_send_get_cap_resp(bt_conn_t *p_conn)
{
UINT32 ret;
hci_data_t *h_buff;
BTD("[avrcp]%s\n", __func__);
ret = l2cap_get_write_buffer(p_conn->avrcp->cid, 15, &h_buff);
if (ret != BT_SUCCESS) {
return BT_NORESOURCES;
}
/*部分代码略。。。*/
return l2cap_write(p_conn->avrcp->cid, h_buff);
}
总结
秉着大胆猜想,小心求证的调试原则,层层抽丝剥茧,逐个排除可疑点,发现AVRCP Get Capabilities的response packet尾部多了4个byte 0x00,竟然会导致电脑端不发送A2DP Media stream,而我测试了几只手机,它们却忽略尾部多余的4 byte 0x00,蓝牙协议栈的调试实在是太有趣了。最开始我还在怀疑是否是Thinkpad蓝牙协议栈做的不太规范,结果发现是因为人家做的太规范了,这里给Thinkpad点赞。