蓝牙开发那些事(4) |
您所在的位置:网站首页 › 蓝牙配置服务为什么跳 › 蓝牙开发那些事(4) |
上一章,留了个尾巴。 先来个数据包格式的图,上图中,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 |