蓝牙开发那些事(4)

您所在的位置:网站首页 蓝牙配置服务为什么跳 蓝牙开发那些事(4)

蓝牙开发那些事(4)

2024-07-11 00:16| 来源: 网络整理| 查看: 265

上一章,留了个尾巴。

先来个数据包格式的图,上图中,packet header和payload header中间都出现了流控(FLOW)位。

我们之间讲HCI命令的时候,提到了HCI transport层的流控。

其实HCI上层的L2CAP层,也提供了流控服务的。

是不是概念很搞?

接下来我们具体讲一下流控。

其实只要有传输就会有流控,在我们蓝牙的体系里,数据的流动有两个关键位置,一是介于controller和host之间的HCI,这个通过hci transport层的流控实现,我们前面讲过hci 命令,本章将继续讲到hci data的流控。

二是空中包,这个流控有可以分为LC层的流控和l2cap层的流控,LC层的流控是主要方式,通过数据包中packet header的流控位实现,l2cap层的流控通过一种特殊的s-frame(rr、rej)来实现,不过一般l2cap层的流控是不打开的,下文将展开讲。

先说packet header中的流控,packet header都是controller中的LC层自动填充的,那么这里的流控位是从哪里来的呢?

既然是LC层的东西,答案自然是controller层。

Controller接收也有一个buffer的概念,自然不可能无限制接收空中包,当controller中的buffer塞满的时候,packet header中的flow位便会置位为stop。

看上去是controller内部的行为,host改变不了对不对?

实际上我们可以做一个简单的实验的,因为数据的流动方向是,空中—>controlleràhost,假如host一直不去controller取数据的时候,空中包自然会把controller塞满。

我们可以利用hci层的流控机制,之前我们在第一章中已经讲述了hci command的流控,这里就要涉及到hci data的流控。

我们知道hci acl data是双向的,由controller流向host的是remote端发过来的数据,由host流向controller的是本地发给peer端的数据。

当打开controller到host方向上的流控的时候,首先host向controller发送host buffer size command通知到controller,大概host可以存放多少包数据,之后,controller向host上传acl data的时候,controller内部会计数,把host的buffer数减去1,当host处理掉一包数据之后,会向controller下发host number of complete packets command,来通知到controller,host释放掉一包数据的空间了,由controller内部计数,把host的buffer数加上1。

假如我们故意不下发host number of complete packets command的时候,controller的内部计数变成0了以后,就无法向host上传acl data数据了。

再过一段时间,controller的接收buffer就会满,此时我们如果看空中包的话,controller回复给远端的数据包,就会看到packet header中的flow位变成stop了。

笔者以前工作中就遇到过这样的情况,host的buffer空间捉襟见肘,只能通过流控的方式来减缓对端发送的速度。

一般来讲,HOST的buffer相对controller来说要大一些,需要使用流控的场合较少。

我们抓包的数据中,就没有controller向host方向的流控,但是host向controller方向的流控是有的。

Host向controller方向的流控的方式有packet based和data block based两种,也大同小异,只是计量方式不同而已,前者是包的个数,后者是包的大小。

我们这次抓的数据包是选择的packet based的方式:

首先host在连接阶段会向controller发送read buffer size的命令

随后controller回复了最多支持的acl data packets的数量是8个。

然后我们看看当host向controller下发acl data的时候会发生什么:

图中标出的两条数据,第一条是向远端发送的一条acl data数据,(protocol栏下面是l2cap的都是这种数据),这个时候协议栈内部是有个计数的,controller的buffer数量从8会减为7。

然后跨越千山万水,在第二个标志处,controller向host返回了number of completed packets event,其中number of completed packets的数目是1,这表示controller处理掉了一包数据,可以释放buffer了,于是controller的buffer数量会从7变为8。

好了,packet header的flow我们讲完了,payload header中也有一个flow位,这是什么鬼?

Lc层的流控相当于一个总阀,l2cap层以上,通过cid区分,建立了多个逻辑链路,每个逻辑链路也可能带有flow功能的,这个flow功能就是l2cap层提供的服务了,我们下一章再讲吧。

接下来,我们看看流控在btstack的体现。

对于controller向host发送的方向来说, 在收到acl data的时候:

static void acl_handler(uint8_t *packet, int size){     // log_info("acl_handler: size %u", size);     // get info     hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);     hci_connection_t *conn      = hci_connection_for_handle(con_handle);     uint8_t  acl_flags          = READ_ACL_FLAGS(packet);     uint16_t acl_length         = READ_ACL_LENGTH(packet);     // ignore non-registered handle     if (!conn){         log_error( "hci.c: acl_handler called with non-registered handle %u!" , con_handle);         return;     }     // assert packet is complete        if ((acl_length + 4) != size){         log_error("hci.c: acl_handler called with ACL packet of wrong size %d, expected %u => dropping packet", size, acl_length + 4);         return;     } #ifdef ENABLE_CLASSIC     // update idle timestamp     hci_connection_timestamp(conn); #endif #ifdef ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL     hci_stack->host_completed_packets = 1;     conn->num_packets_completed++; #endif

对于host_completed_packets置1,然后会回复hci_host_num_completed_packets给controller,再把host_completed_packet置0。

对于host向controller的方向来说,当发送acl data给对端的时候,调用hci_send_acl_packet_fragments函数,其中会执行

   connection->num_packets_sent++;

将hci_connection_t 结构体的num_packets_sent加1,这个值很重要,因为在判断是否可以向controller发送数据的时候,hci_can_send_prepared_acl_packet_now中是调用hci_number_free_acl_slots_for_handle函数去判断的 

   for (it = (btstack_linked_item_t *) hci_stack->connections; it != NULL; it = it->next){         hci_connection_t * connection = (hci_connection_t *) it;         if (hci_is_le_connection(connection)){             num_packets_sent_le += connection->num_packets_sent;         }         if (connection->address_type == BD_ADDR_TYPE_ACL){             num_packets_sent_classic += connection->num_packets_sent;         }     }     log_debug("ACL classic buffers: %u used of %u", num_packets_sent_classic, hci_stack->acl_packets_total_num);     int free_slots_classic = hci_stack->acl_packets_total_num - num_packets_sent_classic;

根据controller报告给host的buffer大小(通过hci read buffer size command取得),减去这个num_packets_sent就是还剩下的buffer,如果buffer 为0自然就不能再发了。

当收到number of completed packets event的时候,这个num_packets_sent就可以释放。

case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:{             if (size < 3) return;             uint16_t num_handles = packet[2];             if (size != (3 + num_handles * 4)) return;             uint16_t offset = 3;             for (i=0; inum_packets_sent >= num_packets){                     conn->num_packets_sent -= num_packets;                 } else {                     log_error("hci_number_completed_packets, more packet slots freed then sent.");                     conn->num_packets_sent = 0;                 }

 

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3