bluedroid源码分析之ACL包发送和接收(二)
更多内容请参照我的个人站点: http://stackvoid.com/
上一节讲了数据流入口,本文分析L2CAP的处理函数。
L2CAP层的处理
我们的音乐数据,通过 L2CAP 入口函数 l2c_data_write 的层层“考验”,已经顺利进入到 L2CAP 里了,下面我们来看看 L2CAP 层具体是怎么处理数据的。
首先我们进入了 L2CAP 层的状态机。
1 void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
2 { 3 switch (p_ccb->chnl_state)
4 { 5 case CST_CLOSED:
6 l2c_csm_closed (p_ccb, event, p_data);
7 break;
8
9 case CST_ORIG_W4_SEC_COMP:
10 l2c_csm_orig_w4_sec_comp (p_ccb, event, p_data);
11 break;
12
13 case CST_TERM_W4_SEC_COMP:
14 l2c_csm_term_w4_sec_comp (p_ccb, event, p_data);
15 break;
16
17 case CST_W4_L2CAP_CONNECT_RSP:
18 l2c_csm_w4_l2cap_connect_rsp (p_ccb, event, p_data);
19 break;
20
21 case CST_W4_L2CA_CONNECT_RSP:
22 l2c_csm_w4_l2ca_connect_rsp (p_ccb, event, p_data);
23 break;
24
25 case CST_CONFIG:
26 l2c_csm_config (p_ccb, event, p_data);
27 break;
28
29 case CST_OPEN:
30 l2c_csm_open (p_ccb, event, p_data);
31 break;
32
33 case CST_W4_L2CAP_DISCONNECT_RSP:
34 l2c_csm_w4_l2cap_disconnect_rsp (p_ccb, event, p_data);
35 break;
36
37 case CST_W4_L2CA_DISCONNECT_RSP:
38 l2c_csm_w4_l2ca_disconnect_rsp (p_ccb, event, p_data);
39 break;
40
41 default:
42 break;
43 }
44 }
具体的 Channel 状态信息如下
1 typedef enum
2 { 3 CST_CLOSED, /* Channel is in clodes state */
4 CST_ORIG_W4_SEC_COMP, /* Originator waits security clearence */
5 CST_TERM_W4_SEC_COMP, /* Acceptor waits security clearence */
6 CST_W4_L2CAP_CONNECT_RSP, /* Waiting for peer conenct response */
7 CST_W4_L2CA_CONNECT_RSP, /* Waiting for upper layer connect rsp */
8 CST_CONFIG, /* Negotiating configuration */
9 CST_OPEN, /* Data transfer state */
10 CST_W4_L2CAP_DISCONNECT_RSP, /* Waiting for peer disconnect rsp */
11 CST_W4_L2CA_DISCONNECT_RSP /* Waiting for upper layer disc rsp */
12 } tL2C_CHNL_STATE;
l2c_csm_execute 函数通过 p_ccb 中的 chnl_state 字段,来确定接下来数据包的走向。如下图所示:
CST_CLOSED 状态:这个 case 处理 Channel 处于 CLOSED 状态的事件。这个状态仅仅存在于 L2CAP 的 Link 初始建立过程中。
如果发现事件是 L2CEVT_LP_DISCONNECT_IND,则当前 Link 已经断开,则释放当前 Channel的 ccb;
- 若事件是 L2CEVT_LP_CONNECT_CFM,则置 p_ccb->chnl_state 为 CST_ORIG_W4_SEC_COMP 状态,下面会接着介绍这个。
- 如果是 L2CEVT_LP_CONNECT_CFM_NEG 则说明当前 Link 失败,
- 如果是 L2CEVT_SEC_COMP 则说明 Security 已经清除成功。
- 若是 L2CEVT_L2CA_CONNECT_REQ 则说明 来自上层的 connect 请求,如果当前处于 sniff 状态,要先取消 sniff。
- L2CEVT_SEC_COMP_NEG 说明 Security 失败,清除当前 CCB,返回
- L2CEVT_L2CAP_CONNECT_REQ 说明是 Peer connect request,既然成功连接了,结束掉 timer
- L2CEVT_L2CA_DATA_WRITE,如果我们的数据从上层经过这里,并且是 CST_CLOSED,由于当前的 Channel 没有建立好,并且上层已经将数据丢给L2CAP了,只能将数据丢弃处理了(数据流不能逆向)。
L2CEVT_L2CA_DISCONNECT_REQ,上层想断开链接,会使用这个 Event来处理。
CST_ORIG_W4_SEC_COMP 状态:Originator(我的理解是 发起 link 建立的应用)等待 security 的间隙,这个间隙需要处理的事件。跟 CST_CLOSED 差不多,源码很容易读,这里不再次做分析。注意,如果是 L2CEVT_SEC_COMP 事件(跟安全相关),会把 p_ccb->chnl_state 置为 CST_W4_L2CAP_CONNECT_RSP
- CST_TERM_W4_SEC_COMP状态:Acceptor(接收者)等待 security 的间隙,源码易读,不再详细展开分析,注意一个事件 L2CEVT_SEC_COMP 的处理,会将 p_ccb->chnl_state 置为 CST_W4_L2CA_CONNECT_RSP
- CST_W4_L2CAP_CONNECT_RSP: 经过 2 或 3 这个关于安全链接的步骤,当然要等待 Peer connect的回应(Response)。分为 获取 peer connect的 confirm、pending、rejected connection等信息,作进一步的判断。
- CST_CONFIG:商讨配置的过程。
- CST_OPEN:Channel 处于 OPEN 状态,我们可以发送上层传过来的数据,我们的音乐数据就是从这个 case 里发出去的。
- CST_W4_L2CAP_DISCONNECT_RSP:等待 peer(对方设备)断开链接的 Response
- CST_W4_L2CA_DISCONNECT_RSP:等待上层 disc rsp
分析了这么一大堆,我们的听音乐那那笔数据包,走的是 CST_OPEN 这个case,因为别的几个 case 在link 建立完成时就走完了。
我们的音乐数据包走的是 CST_OPEN 这个 case,这个 case 调用的是 l2c_csm_open 这个函数,接下来我们继续分析这个函数。
我们的音乐数据包在函数 l2c_csm_open 中流转,经过各种选择和判断,最后走的是 L2CEVT_L2CA_DATA_WRITE 这个 case。这个 case 调用了 l2c_enqueue_peer_data 让数据进入到当前 ccb 的 xmit_hold_q 队列中,暂存此数据包。l2c_link_check_send_pkts 这个函数发送数据包。下面我们会继续分析这两个函数。
1 //l2c_csm_open 处理 Channel 处于 OPEN 状态下的各种 Event
2 static void l2c_csm_open (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
3 { 4 UINT16 local_cid = p_ccb->local_cid;
5 tL2CAP_CFG_INFO *p_cfg;
6 tL2C_CHNL_STATE tempstate;
7 UINT8 tempcfgdone;
8 UINT8 cfg_result;
9
10 #if (BT_TRACE_VERBOSE == TRUE)
11 L2CAP_TRACE_EVENT2 ("L2CAP - LCID: 0x%04x st: OPEN evt: %s", p_ccb->local_cid, l2c_csm_get_event_name (event));
12 #else
13 L2CAP_TRACE_EVENT1 ("L2CAP - st: OPEN evt: %d", event);
14 #endif
15
16 #if (L2CAP_UCD_INCLUDED == TRUE) //默认 UCD 是关闭的
17 if ( local_cid == L2CAP_CONNECTIONLESS_CID )
18 { 19 /* check if this event can be processed by UCD */
20 if ( l2c_ucd_process_event (p_ccb, event, p_data) )
21 { 22 /* The event is processed by UCD state machine */
23 return;
24 }
25 }
26 #endif
27
28 switch (event)
29 { 30 case L2CEVT_LP_DISCONNECT_IND: //Link 都断开连接了,自然 Channel也没有存在的必要了,各种清除 CCB 的工作
31 L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x No Conf Needed", p_ccb->local_cid);
32 l2cu_release_ccb (p_ccb);//释放 当前的 CCB
33 if (p_ccb->p_rcb)
34 (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(local_cid, FALSE);
35 break;
36
37 case L2CEVT_LP_QOS_VIOLATION_IND: /* QOS violation */
38 /* Tell upper layer. If service guaranteed, then clear the channel */
39 if (p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)
40 (*p_ccb->p_rcb->api.pL2CA_QoSViolationInd_Cb)(p_ccb->p_lcb->remote_bd_addr);
41 break;
42
43 case L2CEVT_L2CAP_CONFIG_REQ: /* Peer config request */
44 p_cfg = (tL2CAP_CFG_INFO *)p_data;
45
46 tempstate = p_ccb->chnl_state;
47 tempcfgdone = p_ccb->config_done;
48 p_ccb->chnl_state = CST_CONFIG; //如果数据流中的数据是 L2CEVT_L2CAP_CONFIG_REQ,当然要转到 CST_CONFIG中继续处理
49 p_ccb->config_done &= ~CFG_DONE_MASK;
50 //启动一个 timer ,一段时间后,查看 cfg 的状态
51 //如果配置处于 L2CAP_PEER_CFG_UNACCEPTABLE,继续尝试配置
52 //如果配置处于断开状态,那当前 Channel 直接断开连接。
53 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
54
55 if ((cfg_result = l2cu_process_peer_cfg_req (p_ccb, p_cfg)) == L2CAP_PEER_CFG_OK)
56 { 57 (*p_ccb->p_rcb->api.pL2CA_ConfigInd_Cb)(p_ccb->local_cid, p_cfg);
58 }
59
60 /* Error in config parameters: reset state and config flag */
61 else if (cfg_result == L2CAP_PEER_CFG_UNACCEPTABLE)
62 { 63 btu_stop_timer(&p_ccb->timer_entry);
64 p_ccb->chnl_state = tempstate;
65 p_ccb->config_done = tempcfgdone;
66 l2cu_send_peer_config_rsp (p_ccb, p_cfg);
67 }
68 else /* L2CAP_PEER_CFG_DISCONNECT */
69 { 70 /* Disconnect if channels are incompatible
71 * Note this should not occur if reconfigure
72 * since this should have never passed original config.
73 */
74 l2cu_disconnect_chnl (p_ccb);
75 }
76 break;
77
78 case L2CEVT_L2CAP_DISCONNECT_REQ: /* Peer disconnected request */
79 // btla-specific ++
80 /* Make sure we are not in sniff mode */
81 #if BTM_PWR_MGR_INCLUDED == TRUE
82 { 83 tBTM_PM_PWR_MD settings;
84 settings.mode = BTM_PM_MD_ACTIVE;
85 BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
86 }
87 #else
88 BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
89 #endif
90 // btla-specific --
91
92 p_ccb->chnl_state = CST_W4_L2CA_DISCONNECT_RSP; //Peer 发送 Disconnect,我们要对此发 Response
93 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
94 L2CAP_TRACE_API1 ("L2CAP - Calling Disconnect_Ind_Cb(), CID: 0x%04x Conf Needed", p_ccb->local_cid);
95 (*p_ccb->p_rcb->api.pL2CA_DisconnectInd_Cb)(p_ccb->local_cid, TRUE);
96 break;
97
98 case L2CEVT_L2CAP_DATA: /* Peer data packet rcvd */
99 //收到 Peer 传来的数据,当然要把这个数据通过回调送到上层应用去
100 //pL2CA_DataInd_Cb 中定义了回调,交给上层处理收到的数据
101 (*p_ccb->p_rcb->api.pL2CA_DataInd_Cb)(p_ccb->local_cid, (BT_HDR *)p_data);
102 break;
103
104 case L2CEVT_L2CA_DISCONNECT_REQ: /* Upper wants to disconnect */
105 /* Make sure we are not in sniff mode */
106 #if BTM_PWR_MGR_INCLUDED == TRUE
107 {108 tBTM_PM_PWR_MD settings;
109 settings.mode = BTM_PM_MD_ACTIVE;
110 BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
111 }
112 #else
113 BTM_CancelSniffMode (p_ccb->p_lcb->remote_bd_addr);
114 #endif
115
116 l2cu_send_peer_disc_req (p_ccb);
117 p_ccb->chnl_state = CST_W4_L2CAP_DISCONNECT_RSP;
118 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_DISCONNECT_TOUT);
119 break;
120
121 case L2CEVT_L2CA_DATA_WRITE: /* Upper layer data to send */ //mike mark l2c
122 //上层将数据发送给下层
123 //我们的音乐数据就是走这个 case(为什么?看整个函数的参数就明白了)
124 //首先将数据入队,下面会展开分析这个函数
125 l2c_enqueue_peer_data (p_ccb, (BT_HDR *)p_data);
126 //最终调用 l2c_link_check_send_pkts 来发送我们的音乐数据包
127 l2c_link_check_send_pkts (p_ccb->p_lcb, NULL, NULL);
128 break;
129
130 case L2CEVT_L2CA_CONFIG_REQ: /* Upper layer config req */
131 p_ccb->chnl_state = CST_CONFIG;
132 p_ccb->config_done &= ~CFG_DONE_MASK;
133 l2cu_process_our_cfg_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
134 l2cu_send_peer_config_req (p_ccb, (tL2CAP_CFG_INFO *)p_data);
135 btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_L2CAP_CHNL, L2CAP_CHNL_CFG_TIMEOUT);
136 break;
137
138 case L2CEVT_TIMEOUT:
139 /* Process the monitor/retransmission time-outs in flow control/retrans mode */
140 if (p_ccb->peer_cfg.fcr.mode == L2CAP_FCR_ERTM_MODE)
141 l2c_fcr_proc_tout (p_ccb);
142 break;
143
144 case L2CEVT_ACK_TIMEOUT:
145 l2c_fcr_proc_ack_tout (p_ccb);
146 break;
147 }
148 }
OK,我们下篇将分析数据包入队列的函数。
更多内容请参照我的个人站点: http://stackvoid.com/
bluedroid源码分析之ACL包发送和接收(二)相关推荐
- Android源码分析之广播的发送和接收流程
说明:本文是基于Android6.0源码来分析的 概要 我的理解是,Android中的广播可以看为一种消息机制,用来在一定的条件下触发一些操纵,比如:网络状态的改变,熄屏,亮屏等等Android系统都 ...
- 常用jdk类库源码分析以及各个包
常用jdk类库源码分析以及各个包 1.java.lang包 java.lang包 是Java中最常用的包,程序不需要注入,就可以使用该包中的类,利用包中的类可以设计最基本的Java程序. 2.java ...
- springboot集成mybatis源码分析-启动加载mybatis过程(二)
springboot集成mybatis源码分析-启动加载mybatis过程(二) 1.springboot项目最核心的就是自动加载配置,该功能则依赖的是一个注解@SpringBootApplicati ...
- 源码分析RocketMQ ACL实现机制
有关RocketMQ ACL的使用请查看上一篇<RocketMQ ACL使用指南>,本文从源码的角度,分析一下RocketMQ ACL的实现原理. 备注:RocketMQ在4.4.0时引入 ...
- tcp/ip 协议栈Linux内核源码分析15 udp套接字接收流程二
内核版本:3.4.39 上篇我们分析了UDP套接字如何接收数据的流程,最终它是在内核套接字的接收队列里取出报文,剩下的问题就是谁会去写入这个队列,当然,这部分工作由内核来完成,本篇剩下的文章主要分析内 ...
- spring源码分析-core.io包里面的类
前些日子看<深入理解javaweb开发>时,看到第一章java的io流,发觉自己对io流真的不是很熟悉.然后看了下JDK1.7中io包的一点点代码,又看了org.springframewo ...
- tcp/ip 协议栈Linux内核源码分析14 udp套接字接收流程一
内核版本:3.4.39 前面两篇文章分析了UDP套接字从应用层发送数据到内核层的处理流程,这里继续分析相反的流程,看看数据是怎么从内核送到应用层的. 与发送类似,内核也提供了多个接收数据的系统调用接口 ...
- spring security源码分析之web包分析
Spring 是一个非常流行和成功的 Java 应用开发框架.Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案.一般来说,Web 应用的安全性包括 ...
- spring security源码分析之core包
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架.它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring I ...
最新文章
- ViewGroup的Touch事件分发(源码分析)
- mysql自带的监控报告_MYSQL监控-自带工具Query Profiler的使用
- Silverlight2 边学边练 之三 小球自由落体
- ehcache springboot_Spring Boot应用缓存实践之:Ehcache加持
- 【powerdesigner】将pdm或者cdm保存为普通图片格式
- Flink Checkpoint 问题排查实用指南
- MongoDB清理数据磁盘不释放的解决方法
- ajax异步提交 java_jquery ajax异步上传
- atlas 200 简单确认驱动与固件版本的方法
- 解释HTTP中Get和Post。它们有什么区别,哪个使用时更加安全?
- mysql的R树,GIS空间数据库(17)R+树索引
- 香蜜经典句子摘抄,哪句勾起了你的回忆
- 笔记本电脑把BlackBerry当modem上网
- s3c2440存储控制器详解
- keras中 shape参数如何设置
- CMDN Club #20 活动预告: 从粉笔网多终端产品快速开发说开去——移动创业产品的架构和项目管理(10月10日)...
- 关于回波损耗 和 驻波比的摘要 Return Loss and VSWR
- 中兴N880e升级android4.1版本体验
- 轨道交通行业网站(持续完善)
- [笔记]Windows核心编程《十九》DLL基础