bluedroid源代码分析之ACL包发送和接收(一)
很多其它内容请參照我的个人网站: http://stackvoid.com/
ACL 链路在 Bluetooth 中很重要,一些重要的应用如 A2DP, 基于 RFCOMM 的应用,BNEP等都要建立 ACL 链路,发送/接收ACL 包。今天跟大家一起来分析 ACL 包发送/接收流程,以及涉及到的重要 command/event。
ACL包发送
以下的图(点击大图)是各种应用层使用
L2CAP 的 API:L2CA_DataWrite 发送数据流的过程,此API继续往下走,我仅分析了正常数据流的走向(临时没有考虑别的情况)。

应用层数据到 L2CAP 的入口
我们如果一个听音乐的场景,Mike跟大家一起分析音乐数据流 AVDTP 下面层的传送。
在 AVDTP 中,全部的功能想发送 Data,必须调用 avdt_ad_write_req 这个函数。Mike 就从这个函数入手分析。
1 //当CCB或SCB给l2cap的 Channel 发送数据时。他们终于都会使用到L2CAP的 API:L2CA_Data_Write
2 UINT8 avdt_ad_write_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, BT_HDR *p_buf)
3 {
4 UINT8 tcid;
5
6 /* get tcid from type, scb */
7 tcid = avdt_ad_type_to_tcid(type, p_scb);
8
9
10 return L2CA_DataWrite(avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid, p_buf);
11 }
12 //L2CA_DataWrite的返回形式有三种,各自是:
13 //1. L2CAP_DW_SUCCESS:此数据写成功
14 //2.L2CAP_DW_CONGESTED:写数据成功,可是当前信道拥堵
15 //3.L2CAP_DW_FAILED:写数据失败
16 UINT8 L2CA_DataWrite (UINT16 cid, BT_HDR *p_data)
17 {
18 L2CAP_TRACE_API2 ("L2CA_DataWrite() CID: 0x%04x Len: %d", cid, p_data->len);
19 return l2c_data_write (cid, p_data, L2CAP_FLUSHABLE_CH_BASED);
20 }
当我们的音乐数据流到达 l2c_data_write 这个函数时,标志数据流正式进入到L2CAP层。 我们在以下的源代码中深入分析 l2c_data_write 这个函数。
l2c_data_write 这个函数做的事情主要有:
- 依据參数 cid(Channel ID) 找到 相应的 ccb(Channel Control Block), 找不到返回 L2CAP_DW_FAILED
- 假设測试者 打开 TESTER 这个宏,发送随意数据,当数据大小 大于 MTU 最大值,也会返回 L2CAP_DW_FAILED
- 通过检查 p_ccb->cong_sent 字段,TRUE。则说明当前 Channel 已经拥挤,此时L2CAP的这个Channel不在接收数据,返回 L2CAP_DW_FAILED
- 以上三个条件都通过。说明数据可发送。将数据通过 l2c_csm_execute 继续处理。
进入 l2c_csm_execute 函数,标志着这笔数据已经成功交给 l2CAP 来处理。与上层已经没有关系了。
- l2c_csm_execute 函数运行结束后,再次检查 p_ccb->cong_sent 字段,看看当前的 Channel 是否拥挤,假设拥挤则告诉上层 L2CAP_DW_CONGESTED,否则返回 L2CAP_DW_SUCCESS,表示数据已经成功发送。
1 //返回的数据跟上面的 L2CA_DataWrite 作用同样
2 UINT8 l2c_data_write (UINT16 cid, BT_HDR *p_data, UINT16 flags)
3 {
4 tL2C_CCB *p_ccb;
5
6 //遍历l2cb.ccb_pool。通过Channel ID找到相应的Channel Control Block
7 //l2cu_find_ccb_by_cid 见以下源代码凝视
8 if ((p_ccb = l2cu_find_ccb_by_cid (NULL, cid)) == NULL)
9 {
10 L2CAP_TRACE_WARNING1 ("L2CAP - no CCB for L2CA_DataWrite, CID: %d", cid);
11 GKI_freebuf (p_data);
12 return (L2CAP_DW_FAILED);
13 }
14
15 #ifndef TESTER /* Tester may send any amount of data. otherwise sending message
16 bigger than mtu size of peer is a violation of protocol */
17 if (p_data->len > p_ccb->peer_cfg.mtu)
18 {
19 L2CAP_TRACE_WARNING1 ("L2CAP - CID: 0x%04x cannot send message bigger than peer's mtu size", cid);
20 GKI_freebuf (p_data);
21 return (L2CAP_DW_FAILED);
22 }
23 #endif
24
25 /* channel based, packet based flushable or non-flushable */
26 //Bluedroid中默认的是 L2CAP_FLUSHABLE_CH_BASED
27 //这个 layer_specific 在 数据发送的 l2c_link_send_to_lower 中表示 ACL包分包 个数
28 p_data->layer_specific = flags;
29
30 //发现本 Channel 已经拥堵,直接返回L2CAP_DW_FAILED 告诉上层等会再发数据
31 //当几个应用 共用 此 Channel 可能会出现这样的情况
32 if (p_ccb->cong_sent)
33 {
34 L2CAP_TRACE_ERROR3 ("L2CAP - CID: 0x%04x cannot send, already congested xmit_hold_q.count: %u buff_quota: %u",
35 p_ccb->local_cid, p_ccb->xmit_hold_q.count, p_ccb->buff_quota);
36
37 GKI_freebuf (p_data);
38 return (L2CAP_DW_FAILED);
39 }
40 //毫无疑问啦,这个函数就是我们继续须要分析的函数
41 l2c_csm_execute (p_ccb, L2CEVT_L2CA_DATA_WRITE, p_data);
42
43 //已经将上层的这笔数据发送完,假设此 Channel 拥挤了(之前发送这笔包还没拥挤)
44 //返回 L2CAP_DW_CONGESTED 告诉上层当前信道拥挤,你要给我L2CAP层发数据,是不发下来的
45 if (p_ccb->cong_sent)
46 return (L2CAP_DW_CONGESTED);
47
48 //成功发送。而且此时 Channel 并不拥挤
49 return (L2CAP_DW_SUCCESS);
50 }
51
52 //通过 Channel ID 找到 Channel Control Block
53 tL2C_CCB *l2cu_find_ccb_by_cid (tL2C_LCB *p_lcb, UINT16 local_cid)
54 {
55 tL2C_CCB *p_ccb = NULL;
56 #if (L2CAP_UCD_INCLUDED == TRUE)
57 UINT8 xx;
58 #endif
59
60 if (local_cid >= L2CAP_BASE_APPL_CID) //大于或等于 0x0040 说明不是 Fixed Channel
61 {
62 /* find the associated CCB by "index" */
63 local_cid -= L2CAP_BASE_APPL_CID;
64
65 if (local_cid >= MAX_L2CAP_CHANNELS)
66 return NULL;
67
68 p_ccb = l2cb.ccb_pool + local_cid; //直接通过地址偏移找到
69
70 /* make sure the CCB is in use */
71 if (!p_ccb->in_use)
72 {
73 p_ccb = NULL;
74 }
75 /* make sure it's for the same LCB */
76 else if (p_lcb && p_lcb != p_ccb->p_lcb)
77 {
78 p_ccb = NULL;
79 }
80 }
81 #if (L2CAP_UCD_INCLUDED == TRUE) //默认是关闭的,既然从上层来的都是 数据包了,我觉得不会用到 Fixed Channel
82 else
83 {
84 /* searching fixed channel */
85 p_ccb = l2cb.ccb_pool;
86 for ( xx = 0; xx < MAX_L2CAP_CHANNELS; xx++ )
87 {
88 if ((p_ccb->local_cid == local_cid)
89 &&(p_ccb->in_use)
90 &&(p_lcb == p_ccb->p_lcb))
91 break;
92 else
93 p_ccb++;
94 }
95 if ( xx >= MAX_L2CAP_CHANNELS )
96 return NULL;
97 }
98 #endif
99
100 return (p_ccb);
101 }
下一篇博客我们来看看 L2CAP 层的处理。
bluedroid源代码分析之ACL包发送和接收(一)的更多相关文章
- Mybatis源代码分析之parsing包
parsing,从字面上理解就是编译解析的意思,那么这个包中的内容就应该和mybatis配置文件的编译解析有关系.本文首先会按照引用层次来分别介绍这个包中各个类的作用,而后再用实际的例子解释它们是如何 ...
- L2CAP数据发送和接收
ACL 链路在 Bluetooth 中非常重要,一些重要的应用如 A2DP, 基于 RFCOMM 的应用.BNEP等都要建立 ACL 链路,发送/接收ACL 包.跟大家一起来分析 ACL 包发送/接收 ...
- openVswitch(OVS)源代码分析之工作流程(数据包处理)
上篇分析到数据包的收发,这篇开始着手分析数据包的处理问题.在openVswitch中数据包的处理是其核心技术,该技术分为三部分来实现:第一.根据skb数据包提取相关信息封装成key值:第二.根据提取到 ...
- RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
===================================================== RTMPdump(libRTMP) 源代码分析系列文章: RTMPdump 源代码分析 1: ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...
- 【转载】linux环境下tcpdump源代码分析
linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02 CSDN博客 原文链接 http://blog.csdn.net/han_dawei/article/d ...
- Hadoop源代码分析
http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...
- linux环境下tcpdump源代码分析
Linux 环境下tcpdump 源代码分析 韩大卫@吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分 ...
随机推荐
- mq类----2
手动应答方式 使用get my_consumer.php 消费者 生产者和上一篇 一样 <?php /** * Created by PhpStorm. * User: brady * Dat ...
- mysql处理添加外键时 error 150 问题
当你试图在mysql中创建一个外键的时候,这个出错会经常发生,这是非常令人沮丧的.像这种不能创建一个.frm 文件的报错好像暗示着操作系统的文件的权限错误或者其它原因,但实际上,这些都不是的,事实上, ...
- HDU——1846Brave Game(巴什博弈)
Brave Game Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total ...
- java面试题之你了解守护线程吗?它和非守护线程有什么区别
程序运行完毕,jvm会等待非守护线程完成后关闭,但是jvm不会等待守护线程. 守护线程最典型的的例子是:GC线程
- The reference to entity "characterEncoding" must end with the ';' delimiter (Mybatis + Mysql)
数据源配置时加上编码转换格式后出问题了: The reference to entity "characterEncoding" must end with the ';' del ...
- tyvj 2002 扑克牌
期望DP 本题递推比较麻烦,可以记忆化搜索 注意搜索的边界条件 以及每一次转移 #include <iostream> #include <cstdio> #include & ...
- Java 实现随机验证码
许多系统的注册.登录或者发布信息模块都添加的随机码功能,就是为了避免自动注册程序或者自动发布程序的使用. 验证码实际上就是随机选择一些字符以图片的形式展现在页面上,如果进行提交操作的同时需要将图片上的 ...
- Java 异常处理的优劣
Java编程中的异常处理是一个很常见的话题了,几乎任何一门介绍性的Java课程都会提到异常处理.不过,我认为很多人其实没有真正掌握正确处理异常情况的方法和策略,最多也就不过了解个大概,知道概念.我想对 ...
- 官方Java编码规范
先由Sun制定,之后Sun把Java卖给了Oracle,最后就成了Oracle制定的了.但是版本比较旧了,停留在1999年. 相比Google的编码规范,罪名写的却别就是Sun采用的是4个空格进行缩进 ...
- 黑科技:CSS定制多行省略
转载请注明出处:http://hai.li/2017/03/08/css-multiline-overflow-ellipsis.html 什么是多行省略? 当字数多到一定程度就显示省略号点点点.最初 ...