1 /******************************************************************************
2 *
3 * Copyright 2003-2012 Broadcom Corporation
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 ******************************************************************************/
18
19 /******************************************************************************
20 *
21 * This AVCTP module interfaces to L2CAP
22 *
23 ******************************************************************************/
24
25 #include "avct_api.h"
26 #include "avct_int.h"
27 #include "bt_target.h"
28 #include "bt_types.h"
29 #include "l2c_api.h"
30 #include "l2cdefs.h"
31 #include "osi/include/osi.h"
32
33 /* callback function declarations */
34 void avct_l2c_connect_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
35 uint16_t psm, uint8_t id);
36 void avct_l2c_connect_cfm_cback(uint16_t lcid, uint16_t result);
37 void avct_l2c_config_cfm_cback(uint16_t lcid, uint16_t result,
38 tL2CAP_CFG_INFO* p_cfg);
39 void avct_l2c_config_ind_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg);
40 void avct_l2c_disconnect_ind_cback(uint16_t lcid, bool ack_needed);
41 void avct_l2c_congestion_ind_cback(uint16_t lcid, bool is_congested);
42 void avct_l2c_data_ind_cback(uint16_t lcid, BT_HDR* p_buf);
43 static void avct_on_l2cap_error(uint16_t lcid, uint16_t result);
44
45 /* L2CAP callback function structure */
46 const tL2CAP_APPL_INFO avct_l2c_appl = {
47 avct_l2c_connect_ind_cback, avct_l2c_connect_cfm_cback,
48 avct_l2c_config_ind_cback, avct_l2c_config_cfm_cback,
49 avct_l2c_disconnect_ind_cback, avct_l2c_data_ind_cback,
50 avct_l2c_congestion_ind_cback, NULL,
51 avct_on_l2cap_error, NULL,
52 NULL, NULL,
53 };
54
55 /*******************************************************************************
56 *
57 * Function avct_l2c_is_passive
58 *
59 * Description check is the CCB associated with the given LCB was created
60 * as passive
61 *
62 * Returns true, if the given LCB is created as AVCT_PASSIVE
63 *
64 ******************************************************************************/
avct_l2c_is_passive(tAVCT_LCB * p_lcb)65 static bool avct_l2c_is_passive(tAVCT_LCB* p_lcb) {
66 bool is_passive = false;
67 tAVCT_CCB* p_ccb = &avct_cb.ccb[0];
68 int i;
69
70 for (i = 0; i < AVCT_NUM_CONN; i++, p_ccb++) {
71 if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) {
72 AVCT_TRACE_DEBUG("avct_l2c_is_ct control:x%x", p_ccb->cc.control);
73 if (p_ccb->cc.control & AVCT_PASSIVE) {
74 is_passive = true;
75 break;
76 }
77 }
78 }
79 return is_passive;
80 }
81
82 /*******************************************************************************
83 *
84 * Function avct_l2c_connect_ind_cback
85 *
86 * Description This is the L2CAP connect indication callback function.
87 *
88 *
89 * Returns void
90 *
91 ******************************************************************************/
avct_l2c_connect_ind_cback(const RawAddress & bd_addr,uint16_t lcid,UNUSED_ATTR uint16_t psm,uint8_t id)92 void avct_l2c_connect_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
93 UNUSED_ATTR uint16_t psm, uint8_t id) {
94 tAVCT_LCB* p_lcb;
95 uint16_t result = L2CAP_CONN_OK;
96
97 /* do we already have a channel for this peer? */
98 p_lcb = avct_lcb_by_bd(bd_addr);
99 if (p_lcb == NULL) {
100 /* no, allocate lcb */
101 p_lcb = avct_lcb_alloc(bd_addr);
102 if (p_lcb == NULL) {
103 /* no ccb available, reject L2CAP connection */
104 result = L2CAP_CONN_NO_RESOURCES;
105 }
106 }
107 /* else we already have a channel for this peer */
108 else {
109 if (!avct_l2c_is_passive(p_lcb) || (p_lcb->ch_state == AVCT_CH_OPEN)) {
110 /* this LCB included CT role - reject */
111 result = L2CAP_CONN_NO_RESOURCES;
112 } else {
113 /* TG role only - accept the connection from CT. move the channel ID to
114 * the conflict list */
115 p_lcb->conflict_lcid = p_lcb->ch_lcid;
116 AVCT_TRACE_DEBUG("avct_l2c_connect_ind_cback conflict_lcid:0x%x",
117 p_lcb->conflict_lcid);
118 }
119 }
120
121 if (p_lcb) {
122 AVCT_TRACE_DEBUG("avct_l2c_connect_ind_cback: 0x%x, res: %d, ch_state: %d",
123 lcid, result, p_lcb->ch_state);
124 }
125
126 /* If we reject the connection, send DisconnectReq */
127 if (result != L2CAP_CONN_OK) {
128 L2CA_DisconnectReq(lcid);
129 }
130
131 /* if result ok, proceed with connection */
132 if (result == L2CAP_CONN_OK) {
133 /* store LCID */
134 p_lcb->ch_lcid = lcid;
135
136 /* transition to configuration state */
137 p_lcb->ch_state = AVCT_CH_CFG;
138 }
139
140 if (p_lcb) AVCT_TRACE_DEBUG("ch_state cni: %d ", p_lcb->ch_state);
141 }
142
avct_on_l2cap_error(uint16_t lcid,uint16_t result)143 static void avct_on_l2cap_error(uint16_t lcid, uint16_t result) {
144 tAVCT_LCB* p_lcb = avct_lcb_by_lcid(lcid);
145 if (p_lcb == nullptr) return;
146 if (p_lcb->ch_state == AVCT_CH_CONN) {
147 AVCT_TRACE_DEBUG("avct_l2c_connect_cfm_cback conflict_lcid:0x%x",
148 p_lcb->conflict_lcid);
149 if (p_lcb->conflict_lcid == lcid) {
150 p_lcb->conflict_lcid = 0;
151 } else {
152 tAVCT_LCB_EVT avct_lcb_evt;
153 avct_lcb_evt.result = result;
154 avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, &avct_lcb_evt);
155 }
156 } else if (p_lcb->ch_state == AVCT_CH_CFG) {
157 AVCT_TRACE_DEBUG("ERROR avct_l2c_config_cfm_cback L2CA_DisconnectReq %d ",
158 p_lcb->ch_state);
159 /* store result value */
160 p_lcb->ch_result = result;
161
162 /* Send L2CAP disconnect req */
163 L2CA_DisconnectReq(lcid);
164 }
165 }
166
167 /*******************************************************************************
168 *
169 * Function avct_l2c_connect_cfm_cback
170 *
171 * Description This is the L2CAP connect confirm callback function.
172 *
173 *
174 * Returns void
175 *
176 ******************************************************************************/
avct_l2c_connect_cfm_cback(uint16_t lcid,uint16_t result)177 void avct_l2c_connect_cfm_cback(uint16_t lcid, uint16_t result) {
178 tAVCT_LCB* p_lcb;
179
180 /* look up lcb for this channel */
181 p_lcb = avct_lcb_by_lcid(lcid);
182 if (p_lcb != NULL) {
183 AVCT_TRACE_DEBUG(
184 "avct_l2c_connect_cfm_cback lcid:0x%x result: %d ch_state: %d, "
185 "conflict_lcid:0x%x",
186 lcid, result, p_lcb->ch_state, p_lcb->conflict_lcid);
187 /* if in correct state */
188 if (p_lcb->ch_state == AVCT_CH_CONN) {
189 /* if result successful */
190 if (result == L2CAP_CONN_OK) {
191 /* set channel state */
192 p_lcb->ch_state = AVCT_CH_CFG;
193 }
194 /* else failure */
195 else {
196 LOG(ERROR) << __func__ << ": invoked with non OK status";
197 }
198 } else if (p_lcb->conflict_lcid == lcid) {
199 /* we must be in AVCT_CH_CFG state for the ch_lcid channel */
200 AVCT_TRACE_DEBUG(
201 "avct_l2c_connect_cfm_cback ch_state: %d, conflict_lcid:0x%x",
202 p_lcb->ch_state, p_lcb->conflict_lcid);
203 if (result == L2CAP_CONN_OK) {
204 /* just in case the peer also accepts our connection - Send L2CAP
205 * disconnect req */
206 L2CA_DisconnectReq(lcid);
207 }
208 p_lcb->conflict_lcid = 0;
209 }
210 AVCT_TRACE_DEBUG("ch_state cnc: %d ", p_lcb->ch_state);
211 }
212 }
213
214 /*******************************************************************************
215 *
216 * Function avct_l2c_config_cfm_cback
217 *
218 * Description This is the L2CAP config confirm callback function.
219 *
220 *
221 * Returns void
222 *
223 ******************************************************************************/
avct_l2c_config_cfm_cback(uint16_t lcid,uint16_t initiator,tL2CAP_CFG_INFO * p_cfg)224 void avct_l2c_config_cfm_cback(uint16_t lcid, uint16_t initiator,
225 tL2CAP_CFG_INFO* p_cfg) {
226 avct_l2c_config_ind_cback(lcid, p_cfg);
227
228 tAVCT_LCB* p_lcb;
229
230 /* look up lcb for this channel */
231 p_lcb = avct_lcb_by_lcid(lcid);
232 if (p_lcb != NULL) {
233 AVCT_TRACE_DEBUG("avct_l2c_config_cfm_cback: 0x%x, ch_state: %d,", lcid,
234 p_lcb->ch_state);
235 /* if in correct state */
236 if (p_lcb->ch_state == AVCT_CH_CFG) {
237 p_lcb->ch_state = AVCT_CH_OPEN;
238 avct_lcb_event(p_lcb, AVCT_LCB_LL_OPEN_EVT, NULL);
239 }
240 AVCT_TRACE_DEBUG("ch_state cfc: %d ", p_lcb->ch_state);
241 }
242 }
243
244 /*******************************************************************************
245 *
246 * Function avct_l2c_config_ind_cback
247 *
248 * Description This is the L2CAP config indication callback function.
249 *
250 *
251 * Returns void
252 *
253 ******************************************************************************/
avct_l2c_config_ind_cback(uint16_t lcid,tL2CAP_CFG_INFO * p_cfg)254 void avct_l2c_config_ind_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
255 tAVCT_LCB* p_lcb;
256
257 /* look up lcb for this channel */
258 p_lcb = avct_lcb_by_lcid(lcid);
259 if (p_lcb != NULL) {
260 AVCT_TRACE_DEBUG("avct_l2c_config_ind_cback: 0x%x, ch_state: %d", lcid,
261 p_lcb->ch_state);
262 /* store the mtu in tbl */
263 if (p_cfg->mtu_present) {
264 p_lcb->peer_mtu = p_cfg->mtu;
265 } else {
266 p_lcb->peer_mtu = L2CAP_DEFAULT_MTU;
267 }
268 }
269 }
270
271 /*******************************************************************************
272 *
273 * Function avct_l2c_disconnect_ind_cback
274 *
275 * Description This is the L2CAP disconnect indication callback function.
276 *
277 *
278 * Returns void
279 *
280 ******************************************************************************/
avct_l2c_disconnect_ind_cback(uint16_t lcid,bool ack_needed)281 void avct_l2c_disconnect_ind_cback(uint16_t lcid, bool ack_needed) {
282 tAVCT_LCB* p_lcb;
283 uint16_t result = AVCT_RESULT_FAIL;
284
285 /* look up lcb for this channel */
286 p_lcb = avct_lcb_by_lcid(lcid);
287 if (p_lcb != NULL) {
288 AVCT_TRACE_DEBUG("avct_l2c_disconnect_ind_cback: 0x%x, ch_state: %d", lcid,
289 p_lcb->ch_state);
290 tAVCT_LCB_EVT avct_lcb_evt;
291 avct_lcb_evt.result = result;
292 avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, &avct_lcb_evt);
293 AVCT_TRACE_DEBUG("ch_state di: %d ", p_lcb->ch_state);
294 }
295 }
296
avct_l2c_disconnect(uint16_t lcid,uint16_t result)297 void avct_l2c_disconnect(uint16_t lcid, uint16_t result) {
298 L2CA_DisconnectReq(lcid);
299
300 tAVCT_LCB* p_lcb;
301 uint16_t res;
302
303 /* look up lcb for this channel */
304 p_lcb = avct_lcb_by_lcid(lcid);
305 if (p_lcb != NULL) {
306 AVCT_TRACE_DEBUG(
307 "avct_l2c_disconnect_cfm_cback: 0x%x, ch_state: %d, res: %d", lcid,
308 p_lcb->ch_state, result);
309 /* result value may be previously stored */
310 res = (p_lcb->ch_result != 0) ? p_lcb->ch_result : result;
311 p_lcb->ch_result = 0;
312
313 tAVCT_LCB_EVT avct_lcb_evt;
314 avct_lcb_evt.result = res;
315 avct_lcb_event(p_lcb, AVCT_LCB_LL_CLOSE_EVT, &avct_lcb_evt);
316 AVCT_TRACE_DEBUG("ch_state dc: %d ", p_lcb->ch_state);
317 }
318 }
319
320 /*******************************************************************************
321 *
322 * Function avct_l2c_congestion_ind_cback
323 *
324 * Description This is the L2CAP congestion indication callback function.
325 *
326 *
327 * Returns void
328 *
329 ******************************************************************************/
avct_l2c_congestion_ind_cback(uint16_t lcid,bool is_congested)330 void avct_l2c_congestion_ind_cback(uint16_t lcid, bool is_congested) {
331 tAVCT_LCB* p_lcb;
332
333 AVCT_TRACE_DEBUG("avct_l2c_congestion_ind_cback: 0x%x", lcid);
334 /* look up lcb for this channel */
335 p_lcb = avct_lcb_by_lcid(lcid);
336 if (p_lcb != NULL) {
337 tAVCT_LCB_EVT avct_lcb_evt;
338 avct_lcb_evt.cong = is_congested;
339 avct_lcb_event(p_lcb, AVCT_LCB_LL_CONG_EVT, &avct_lcb_evt);
340 }
341 }
342
343 /*******************************************************************************
344 *
345 * Function avct_l2c_data_ind_cback
346 *
347 * Description This is the L2CAP data indication callback function.
348 *
349 *
350 * Returns void
351 *
352 ******************************************************************************/
avct_l2c_data_ind_cback(uint16_t lcid,BT_HDR * p_buf)353 void avct_l2c_data_ind_cback(uint16_t lcid, BT_HDR* p_buf) {
354 tAVCT_LCB* p_lcb;
355
356 AVCT_TRACE_DEBUG("avct_l2c_data_ind_cback: 0x%x", lcid);
357 /* look up lcb for this channel */
358 p_lcb = avct_lcb_by_lcid(lcid);
359 if (p_lcb != NULL) {
360 avct_lcb_event(p_lcb, AVCT_LCB_LL_MSG_EVT, (tAVCT_LCB_EVT*)&p_buf);
361 } else /* prevent buffer leak */
362 {
363 AVCT_TRACE_WARNING("ERROR -> avct_l2c_data_ind_cback drop buffer");
364 osi_free(p_buf);
365 }
366 }
367