1 /******************************************************************************
2 *
3 * Copyright (C) 2016 The Android Open Source Project
4 * Copyright (C) 2002-2012 Broadcom Corporation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at:
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************************/
19 /******************************************************************************
20 *
21 * this file contains the connection interface functions
22 *
23 ******************************************************************************/
24 #include "btm_int.h"
25 #include "hid_conn.h"
26 #include "hid_int.h"
27 #include "osi/allocator.h"
28 #include "osi/osi.h"
29 #include "stack/btm_api.h"
30 #include "stack/btu.h"
31 #include "stack/hidd_api.h"
32 #include "stack/hiddefs.h"
33 #include "stack/l2c_api.h"
34 #include "stack/l2cdefs.h"
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #if (HID_DEV_INCLUDED == TRUE)
40
41 static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id);
42 static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result);
43 static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg);
44 static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg);
45 static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed);
46 static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result);
47 static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg);
48 static void hidd_l2cif_cong_ind(uint16_t cid, bool congested);
49
50 static const tL2CAP_APPL_INFO dev_reg_info = {hidd_l2cif_connect_ind,
51 hidd_l2cif_connect_cfm,
52 NULL,
53 hidd_l2cif_config_ind,
54 hidd_l2cif_config_cfm,
55 hidd_l2cif_disconnect_ind,
56 hidd_l2cif_disconnect_cfm,
57 NULL,
58 hidd_l2cif_data_ind,
59 hidd_l2cif_cong_ind,
60 NULL};
61 /*******************************************************************************
62 *
63 * Function hidd_check_config_done
64 *
65 * Description Checks if connection is configured and callback can be fired
66 *
67 * Returns void
68 *
69 ******************************************************************************/
hidd_check_config_done(void)70 static void hidd_check_config_done(void)
71 {
72 tHID_CONN *p_hcon;
73 p_hcon = &hd_cb.device.conn;
74 if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) &&
75 (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) {
76 p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
77 hd_cb.device.state = HIDD_DEV_CONNECTED;
78 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_OPEN, 0, NULL);
79 // send outstanding data on intr
80 if (hd_cb.pending_data) {
81 L2CA_DataWrite(p_hcon->intr_cid, hd_cb.pending_data);
82 hd_cb.pending_data = NULL;
83 }
84 }
85 }
86 /*******************************************************************************
87 *
88 * Function hidh_sec_check_complete_term
89 *
90 * Description HID security check complete callback function.
91 *
92 * Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise
93 * send security block L2C connection response.
94 *
95 ******************************************************************************/
hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr,UNUSED_ATTR tBT_TRANSPORT transport,void * p_ref_data,uint8_t res)96 static void hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data,
97 uint8_t res)
98 {
99 tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data;
100 if (res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY) {
101 p_dev->conn.disc_reason = HID_SUCCESS;
102 p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
103 L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
104 L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg);
105 } else if (res != BTM_SUCCESS) {
106 HIDD_TRACE_WARNING("%s: connection rejected by security", __func__);
107 p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;
108 p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
109 L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK,
110 L2CAP_CONN_OK);
111 return;
112 }
113 }
114 /*******************************************************************************
115 *
116 * Function hidd_sec_check_complete_orig
117 *
118 * Description HID security check complete callback function (device
119 *originated)
120 *
121 * Returns void
122 *
123 ******************************************************************************/
hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr,UNUSED_ATTR tBT_TRANSPORT transport,void * p_ref_data,uint8_t res)124 void hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data,
125 uint8_t res)
126 {
127 tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data;
128 if (p_dev->conn.conn_state != HID_CONN_STATE_SECURITY) {
129 HIDD_TRACE_WARNING("%s: invalid state (%02x)", __func__, p_dev->conn.conn_state);
130 return;
131 }
132 if (res == BTM_SUCCESS) {
133 HIDD_TRACE_EVENT("%s: security ok", __func__);
134 p_dev->conn.disc_reason = HID_SUCCESS;
135 p_dev->conn.conn_state = HID_CONN_STATE_CONFIG;
136 L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg);
137 } else {
138 HIDD_TRACE_WARNING("%s: security check failed (%02x)", __func__, res);
139 p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;
140 hidd_conn_disconnect();
141 }
142 }
143 /*******************************************************************************
144 *
145 * Function hidd_l2cif_connect_ind
146 *
147 * Description Handles incoming L2CAP connection (we act as server)
148 *
149 * Returns void
150 *
151 ******************************************************************************/
hidd_l2cif_connect_ind(BD_ADDR bd_addr,uint16_t cid,uint16_t psm,uint8_t id)152 static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id)
153 {
154 tHID_CONN *p_hcon;
155 tHID_DEV_DEV_CTB *p_dev;
156 bool accept = TRUE; // accept by default
157 HIDD_TRACE_EVENT("%s: psm=%04x cid=%04x id=%02x", __func__, psm, cid, id);
158 p_dev = &hd_cb.device;
159 if (!hd_cb.allow_incoming) {
160 HIDD_TRACE_WARNING("%s: incoming connections not allowed, rejecting", __func__);
161 L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
162 return;
163 }
164 if (p_dev->in_use && memcmp(bd_addr, p_dev->addr, sizeof(BD_ADDR))) {
165 HIDD_TRACE_WARNING("%s: incoming connections from different device, rejecting", __func__);
166 L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
167 return;
168 } else if (!p_dev->in_use) {
169 p_dev->in_use = TRUE;
170 memcpy(p_dev->addr, bd_addr, sizeof(BD_ADDR));
171 p_dev->state = HIDD_DEV_NO_CONN;
172 }
173 p_hcon = &hd_cb.device.conn;
174 switch (psm) {
175 case HID_PSM_INTERRUPT:
176 if (p_hcon->ctrl_cid == 0) {
177 accept = FALSE;
178 HIDD_TRACE_WARNING("%s: incoming INTR without CTRL, rejecting", __func__);
179 }
180 if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) {
181 accept = FALSE;
182 HIDD_TRACE_WARNING("%s: incoming INTR in invalid state (%d), rejecting", __func__, p_hcon->conn_state);
183 }
184 break;
185 case HID_PSM_CONTROL:
186 if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) {
187 accept = FALSE;
188 HIDD_TRACE_WARNING("%s: incoming CTRL in invalid state (%d), rejecting", __func__, p_hcon->conn_state);
189 }
190 break;
191 default:
192 accept = FALSE;
193 HIDD_TRACE_ERROR("%s: received invalid PSM, rejecting", __func__);
194 break;
195 }
196 if (!accept) {
197 L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
198 return;
199 }
200 // for CTRL we need to go through security and we reply in callback from there
201 if (psm == HID_PSM_CONTROL) {
202 p_hcon->conn_flags = 0;
203 p_hcon->ctrl_cid = cid;
204 p_hcon->ctrl_id = id;
205 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
206 p_hcon->conn_state = HID_CONN_STATE_SECURITY;
207 if (btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN,
208 &hidd_sec_check_complete, p_dev) == BTM_CMD_STARTED) {
209 L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK);
210 }
211 return;
212 }
213 // for INTR we go directly to config state
214 p_hcon->conn_state = HID_CONN_STATE_CONFIG;
215 p_hcon->intr_cid = cid;
216 L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
217 L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg);
218 }
219 /*******************************************************************************
220 *
221 * Function hidd_l2cif_connect_cfm
222 *
223 * Description Handles L2CAP connection response (we act as client)
224 *
225 * Returns void
226 *
227 ******************************************************************************/
hidd_l2cif_connect_cfm(uint16_t cid,uint16_t result)228 static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result)
229 {
230 tHID_DEV_DEV_CTB *p_dev = &hd_cb.device;
231 tHID_CONN *p_hcon = &hd_cb.device.conn;
232 HIDD_TRACE_EVENT("%s: cid=%04x result=%d, conn_state=%d", __func__, cid, result, p_hcon->conn_state);
233 if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
234 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
235 return;
236 }
237 if (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) ||
238 ((cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR))) ||
239 ((cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) {
240 HIDD_TRACE_WARNING("%s: unexpected, cid:0x%04x, ctrl_cid:0x%04x, intr_cid:0x%04x, conn_state:%d", __func__, cid,
241 p_hcon->ctrl_cid, p_hcon->intr_cid, p_hcon->conn_state);
242 return;
243 }
244 if (result != L2CAP_CONN_OK) {
245 HIDD_TRACE_WARNING("%s: connection failed, now disconnect", __func__);
246 if (cid == p_hcon->ctrl_cid) {
247 p_hcon->ctrl_cid = 0;
248 } else {
249 p_hcon->intr_cid = 0;
250 }
251
252 hidd_conn_disconnect();
253 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_L2CAP_CONN_FAIL | (uint32_t)result, NULL);
254 return;
255 }
256 /* CTRL connect conf */
257 if (cid == p_hcon->ctrl_cid) {
258 p_hcon->conn_state = HID_CONN_STATE_SECURITY;
259 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* in case disconnected before sec completed */
260 btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, TRUE, BTM_SEC_PROTO_HID, HIDD_SEC_CHN,
261 &hidd_sec_check_complete_orig, p_dev);
262 } else {
263 p_hcon->conn_state = HID_CONN_STATE_CONFIG;
264 L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg);
265 }
266 return;
267 }
268 /*******************************************************************************
269 *
270 * Function hidd_l2cif_config_ind
271 *
272 * Description Handles incoming L2CAP configuration request
273 *
274 * Returns void
275 *
276 ******************************************************************************/
hidd_l2cif_config_ind(uint16_t cid,tL2CAP_CFG_INFO * p_cfg)277 static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg)
278 {
279 tHID_CONN *p_hcon;
280 HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid);
281 p_hcon = &hd_cb.device.conn;
282 if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
283 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
284 return;
285 }
286 if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_DEV_MTU_SIZE))
287 p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE;
288 else
289 p_hcon->rem_mtu_size = p_cfg->mtu;
290 // accept without changes
291 p_cfg->flush_to_present = FALSE;
292 p_cfg->mtu_present = FALSE;
293 p_cfg->result = L2CAP_CFG_OK;
294 if (cid == p_hcon->intr_cid && hd_cb.use_in_qos && !p_cfg->qos_present) {
295 p_cfg->qos_present = TRUE;
296 memcpy(&p_cfg->qos, &hd_cb.in_qos, sizeof(FLOW_SPEC));
297 }
298 L2CA_ConfigRsp(cid, p_cfg);
299 // update flags
300 if (cid == p_hcon->ctrl_cid) {
301 p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE;
302 if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE) &&
303 (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)) {
304 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
305 if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
306 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
307 hidd_conn_disconnect();
308 HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__);
309 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
310 return;
311 } else {
312 p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
313 }
314 }
315 } else {
316 p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE;
317 }
318 hidd_check_config_done();
319 }
320 /*******************************************************************************
321 *
322 * Function hidd_l2cif_config_cfm
323 *
324 * Description Handles incoming L2CAP configuration response
325 *
326 * Returns void
327 *
328 ******************************************************************************/
hidd_l2cif_config_cfm(uint16_t cid,tL2CAP_CFG_INFO * p_cfg)329 static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg)
330 {
331 tHID_CONN *p_hcon;
332 uint32_t reason;
333 HIDD_TRACE_EVENT("%s: cid=%04x pcfg->result=%d", __func__, cid, p_cfg->result);
334 p_hcon = &hd_cb.device.conn;
335 if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
336 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
337 return;
338 }
339 if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS && p_cfg->qos_present) {
340 tL2CAP_CFG_INFO new_qos;
341 // QoS parameters not accepted for intr, try again with host proposal
342 memcpy(&new_qos, &hd_cb.l2cap_intr_cfg, sizeof(new_qos));
343 memcpy(&new_qos.qos, &p_cfg->qos, sizeof(FLOW_SPEC));
344 new_qos.qos_present = TRUE;
345 HIDD_TRACE_WARNING("%s: config failed, retry", __func__);
346 L2CA_ConfigReq(cid, &new_qos);
347 return;
348 } else if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNKNOWN_OPTIONS) {
349 // QoS not understood by remote device, try configuring without QoS
350 HIDD_TRACE_WARNING("%s: config failed, retry without QoS", __func__);
351 L2CA_ConfigReq(cid, &hd_cb.l2cap_cfg);
352 return;
353 } else if (p_cfg->result != L2CAP_CFG_OK) {
354 HIDD_TRACE_WARNING("%s: config failed, disconnecting", __func__);
355 hidd_conn_disconnect();
356 reason = HID_L2CAP_CFG_FAIL | (uint32_t)p_cfg->result;
357 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, reason, NULL);
358 return;
359 }
360 // update flags
361 if (cid == p_hcon->ctrl_cid) {
362 p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE;
363 if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE) &&
364 (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)) {
365 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
366 if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
367 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
368 hidd_conn_disconnect();
369 HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__);
370 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
371 return;
372 } else {
373 p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
374 }
375 }
376 } else {
377 p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE;
378 }
379 hidd_check_config_done();
380 }
381 /*******************************************************************************
382 *
383 * Function hidd_l2cif_disconnect_ind
384 *
385 * Description Handler incoming L2CAP disconnection request
386 *
387 * Returns void
388 *
389 ******************************************************************************/
hidd_l2cif_disconnect_ind(uint16_t cid,bool ack_needed)390 static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed)
391 {
392 tHID_CONN *p_hcon;
393 HIDD_TRACE_EVENT("%s: cid=%04x ack_needed=%d", __func__, cid, ack_needed);
394 p_hcon = &hd_cb.device.conn;
395 if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
396 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
397 return;
398 }
399
400 if (ack_needed) {
401 L2CA_DisconnectRsp(cid);
402 }
403
404 if (cid == p_hcon->ctrl_cid) {
405 p_hcon->ctrl_cid = 0;
406 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL;
407 } else {
408 p_hcon->intr_cid = 0;
409 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR;
410 }
411 if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {
412 HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__);
413 // clean any outstanding data on intr
414 if (hd_cb.pending_data) {
415 osi_free(hd_cb.pending_data);
416 hd_cb.pending_data = NULL;
417 }
418 hd_cb.device.state = HIDD_DEV_NO_CONN;
419 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
420 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL);
421 }
422 }
423 /*******************************************************************************
424 *
425 * Function hidd_l2cif_disconnect_cfm
426 *
427 * Description Handles L2CAP disconnection response
428 *
429 * Returns void
430 *
431 ******************************************************************************/
hidd_l2cif_disconnect_cfm(uint16_t cid,uint16_t result)432 static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result)
433 {
434 tHID_CONN *p_hcon;
435 HIDD_TRACE_EVENT("%s: cid=%04x result=%d", __func__, cid, result);
436 p_hcon = &hd_cb.device.conn;
437 if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
438 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
439 return;
440 }
441 if (cid == p_hcon->ctrl_cid) {
442 p_hcon->ctrl_cid = 0;
443 } else {
444 p_hcon->intr_cid = 0;
445 // now disconnect CTRL
446 L2CA_DisconnectReq(p_hcon->ctrl_cid);
447 }
448 if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {
449 HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__);
450 hd_cb.device.state = HIDD_DEV_NO_CONN;
451 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
452 if (hd_cb.pending_vc_unplug) {
453 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG, p_hcon->disc_reason, NULL);
454 hd_cb.pending_vc_unplug = FALSE;
455 } else {
456 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL);
457 }
458 }
459 }
460 /*******************************************************************************
461 *
462 * Function hidd_l2cif_cong_ind
463 *
464 * Description Handles L2CAP congestion status event
465 *
466 * Returns void
467 *
468 ******************************************************************************/
hidd_l2cif_cong_ind(uint16_t cid,bool congested)469 static void hidd_l2cif_cong_ind(uint16_t cid, bool congested)
470 {
471 tHID_CONN *p_hcon;
472 HIDD_TRACE_EVENT("%s: cid=%04x congested=%d", __func__, cid, congested);
473 p_hcon = &hd_cb.device.conn;
474 if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
475 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
476 return;
477 }
478 if (congested) {
479 p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED;
480 } else {
481 p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED;
482 }
483 }
484 /*******************************************************************************
485 *
486 * Function hidd_l2cif_data_ind
487 *
488 * Description Handler incoming data on L2CAP channel
489 *
490 * Returns void
491 *
492 ******************************************************************************/
hidd_l2cif_data_ind(uint16_t cid,BT_HDR * p_msg)493 static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg)
494 {
495 tHID_CONN *p_hcon;
496 uint8_t *p_data = (uint8_t *)(p_msg + 1) + p_msg->offset;
497 uint8_t msg_type, param;
498 bool err = FALSE;
499 HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid);
500 p_hcon = &hd_cb.device.conn;
501 if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
502 HIDD_TRACE_WARNING("%s: unknown cid=%04x", __func__, cid);
503 osi_free(p_msg);
504 return;
505 }
506 msg_type = HID_GET_TRANS_FROM_HDR(*p_data);
507 param = HID_GET_PARAM_FROM_HDR(*p_data);
508 if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) {
509 // skip HID header
510 p_msg->offset++;
511 p_msg->len--;
512 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_INTR_DATA, 0, p_msg);
513 return;
514 }
515 switch (msg_type) {
516 case HID_TRANS_GET_REPORT:
517 // at this stage we don't know if Report Id shall be included in request
518 // so we pass complete packet in callback and let other code analyze this
519 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_GET_REPORT, !!(param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS), p_msg);
520 break;
521 case HID_TRANS_SET_REPORT:
522 // as above
523 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_REPORT, 0, p_msg);
524 break;
525 case HID_TRANS_GET_IDLE:
526 hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, hd_cb.device.idle_time, 0, NULL);
527 osi_free(p_msg);
528 break;
529 case HID_TRANS_SET_IDLE:
530 if (p_msg->len != 2) {
531 HIDD_TRACE_ERROR("%s: invalid len (%d) set idle request received", __func__, p_msg->len);
532 err = TRUE;
533 } else {
534 hd_cb.device.idle_time = p_data[1];
535 HIDD_TRACE_DEBUG("%s: idle_time = %d", __func__, hd_cb.device.idle_time);
536 if (hd_cb.device.idle_time) {
537 HIDD_TRACE_WARNING("%s: idle_time of %d ms not supported by HID Device", __func__,
538 (hd_cb.device.idle_time * 4));
539 err = TRUE;
540 }
541 }
542 if (!err) {
543 hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL);
544 } else {
545 hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM, 0, 0, NULL);
546 }
547 osi_free(p_msg);
548 break;
549 case HID_TRANS_GET_PROTOCOL:
550 hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, !hd_cb.device.boot_mode, 0, NULL);
551 osi_free(p_msg);
552 break;
553 case HID_TRANS_SET_PROTOCOL:
554 hd_cb.device.boot_mode = !(param & HID_PAR_PROTOCOL_MASK);
555 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_PROTOCOL, param & HID_PAR_PROTOCOL_MASK, NULL);
556 hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL);
557 osi_free(p_msg);
558 break;
559 case HID_TRANS_CONTROL:
560 switch (param) {
561 case HID_PAR_CONTROL_SUSPEND:
562 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SUSPEND, 0, NULL);
563 break;
564 case HID_PAR_CONTROL_EXIT_SUSPEND:
565 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_EXIT_SUSPEND, 0, NULL);
566 break;
567 case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG:
568 hidd_conn_disconnect();
569 // set flag so we can notify properly when disconnected
570 hd_cb.pending_vc_unplug = TRUE;
571 break;
572 }
573 osi_free(p_msg);
574 break;
575 case HID_TRANS_DATA:
576 default:
577 HIDD_TRACE_WARNING("%s: got unsupported msg (%d)", __func__, msg_type);
578 hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ, 0, 0, NULL);
579 osi_free(p_msg);
580 break;
581 }
582 }
583 /*******************************************************************************
584 *
585 * Function hidd_conn_reg
586 *
587 * Description Registers L2CAP channels
588 *
589 * Returns void
590 *
591 ******************************************************************************/
hidd_conn_reg(void)592 tHID_STATUS hidd_conn_reg(void)
593 {
594 HIDD_TRACE_API("%s", __func__);
595 memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
596 hd_cb.l2cap_cfg.mtu_present = TRUE;
597 hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE;
598 hd_cb.l2cap_cfg.flush_to_present = TRUE;
599 hd_cb.l2cap_cfg.flush_to = HID_DEV_FLUSH_TO;
600 memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO));
601 hd_cb.l2cap_intr_cfg.mtu_present = TRUE;
602 hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE;
603 hd_cb.l2cap_intr_cfg.flush_to_present = TRUE;
604 hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO;
605 if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO *)&dev_reg_info)) {
606 HIDD_TRACE_ERROR("HID Control (device) registration failed");
607 return (HID_ERR_L2CAP_FAILED);
608 }
609 if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *)&dev_reg_info)) {
610 L2CA_Deregister(HID_PSM_CONTROL);
611 HIDD_TRACE_ERROR("HID Interrupt (device) registration failed");
612 return (HID_ERR_L2CAP_FAILED);
613 }
614 return (HID_SUCCESS);
615 }
616 /*******************************************************************************
617 *
618 * Function hidd_conn_dereg
619 *
620 * Description Deregisters L2CAP channels
621 *
622 * Returns void
623 *
624 ******************************************************************************/
hidd_conn_dereg(void)625 void hidd_conn_dereg(void)
626 {
627 HIDD_TRACE_API("%s", __func__);
628 L2CA_Deregister(HID_PSM_CONTROL);
629 L2CA_Deregister(HID_PSM_INTERRUPT);
630 }
631 /*******************************************************************************
632 *
633 * Function hidd_conn_initiate
634 *
635 * Description Initiates HID connection to plugged device
636 *
637 * Returns HID_SUCCESS
638 *
639 ******************************************************************************/
hidd_conn_initiate(void)640 tHID_STATUS hidd_conn_initiate(void)
641 {
642 tHID_DEV_DEV_CTB *p_dev = &hd_cb.device;
643 HIDD_TRACE_API("%s", __func__);
644 if (!p_dev->in_use) {
645 HIDD_TRACE_WARNING("%s: no virtual cable established", __func__);
646 return (HID_ERR_NOT_REGISTERED);
647 }
648 if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) {
649 HIDD_TRACE_WARNING("%s: connection already in progress", __func__);
650 return (HID_ERR_CONN_IN_PROCESS);
651 }
652 p_dev->conn.ctrl_cid = 0;
653 p_dev->conn.intr_cid = 0;
654 p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL;
655 p_dev->conn.conn_flags |= HID_CONN_FLAGS_IS_ORIG;
656 BTM_SetOutService(p_dev->addr, BTM_SEC_SERVICE_HIDD_SEC_CTRL, HIDD_SEC_CHN);
657 /* Check if L2CAP started the connection process */
658 if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq(HID_PSM_CONTROL, p_dev->addr)) == 0) {
659 HIDD_TRACE_WARNING("%s: could not start L2CAP connection", __func__);
660 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
661 } else {
662 p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;
663 }
664 return (HID_SUCCESS);
665 }
666 /*******************************************************************************
667 *
668 * Function hidd_conn_disconnect
669 *
670 * Description Disconnects existing HID connection
671 *
672 * Returns HID_SUCCESS
673 *
674 ******************************************************************************/
hidd_conn_disconnect(void)675 tHID_STATUS hidd_conn_disconnect(void)
676 {
677 tHID_CONN *p_hcon;
678 HIDD_TRACE_API("%s", __func__);
679 // clean any outstanding data on intr
680 if (hd_cb.pending_data) {
681 osi_free(hd_cb.pending_data);
682 hd_cb.pending_data = NULL;
683 }
684 p_hcon = &hd_cb.device.conn;
685 if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) {
686 /* Set l2cap idle timeout to 0 (so ACL link is disconnected
687 * immediately after last channel is closed) */
688 L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR);
689 if (p_hcon->intr_cid) {
690 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR;
691 L2CA_DisconnectReq(p_hcon->intr_cid);
692 } else if (p_hcon->ctrl_cid) {
693 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL;
694 L2CA_DisconnectReq(p_hcon->ctrl_cid);
695 }
696 } else {
697 HIDD_TRACE_WARNING("%s: already disconnected", __func__);
698 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
699 }
700 return (HID_SUCCESS);
701 }
702 /*******************************************************************************
703 *
704 * Function hidd_conn_send_data
705 *
706 * Description Sends data to host
707 *
708 * Returns tHID_STATUS
709 *
710 ******************************************************************************/
hidd_conn_send_data(uint8_t channel,uint8_t msg_type,uint8_t param,uint8_t data,uint16_t len,uint8_t * p_data)711 tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len,
712 uint8_t *p_data)
713 {
714 tHID_CONN *p_hcon;
715 BT_HDR *p_buf;
716 uint8_t *p_out;
717 uint16_t cid;
718 uint16_t buf_size;
719
720 HIDD_TRACE_VERBOSE("%s: channel(%d), msg_type(%d), len(%d)", __func__, channel, msg_type, len);
721
722 p_hcon = &hd_cb.device.conn;
723 if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {
724 return HID_ERR_CONGESTED;
725 }
726
727 switch (msg_type) {
728 case HID_TRANS_HANDSHAKE:
729 case HID_TRANS_CONTROL:
730 cid = p_hcon->ctrl_cid;
731 buf_size = HID_CONTROL_BUF_SIZE;
732 break;
733 case HID_TRANS_DATA:
734 if (channel == HID_CHANNEL_CTRL) {
735 cid = p_hcon->ctrl_cid;
736 buf_size = HID_CONTROL_BUF_SIZE;
737 } else {
738 cid = p_hcon->intr_cid;
739 buf_size = HID_INTERRUPT_BUF_SIZE;
740 }
741 break;
742 default:
743 return (HID_ERR_INVALID_PARAM);
744 }
745 p_buf = (BT_HDR *)osi_malloc(buf_size);
746 if (p_buf == NULL)
747 return (HID_ERR_NO_RESOURCES);
748 p_buf->offset = L2CAP_MIN_OFFSET;
749 p_out = (uint8_t *)(p_buf + 1) + p_buf->offset;
750 *p_out = HID_BUILD_HDR(msg_type, param);
751 p_out++;
752 p_buf->len = 1; // start with header only
753 // add report id prefix only if non-zero (which is reserved)
754 if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) {
755 *p_out = data; // report_id
756 p_out++;
757 p_buf->len++;
758 }
759 if (len > 0 && p_data != NULL) {
760 memcpy(p_out, p_data, len);
761 p_buf->len += len;
762 }
763 // check if connected
764 if (hd_cb.device.state != HIDD_DEV_CONNECTED) {
765 // for DATA on intr we hold transfer and try to reconnect
766 if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) {
767 // drop previous data, we do not queue it for now
768 if (hd_cb.pending_data) {
769 osi_free(hd_cb.pending_data);
770 }
771 hd_cb.pending_data = p_buf;
772 if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) {
773 HIDD_TRACE_WARNING("%s: try to reconnect!", __func__);
774 return hidd_conn_initiate();
775 }
776 return HID_SUCCESS;
777 }
778 return HID_ERR_NO_CONNECTION;
779 }
780 #ifdef REPORT_TRANSFER_TIMESTAMP
781 if (report_transfer) {
782 HIDD_TRACE_ERROR("%s: report sent", __func__);
783 }
784 #endif
785 HIDD_TRACE_VERBOSE("%s: report sent", __func__);
786 if (L2CA_DataWrite(cid, p_buf) == L2CAP_DW_FAILED)
787 return (HID_ERR_CONGESTED);
788 return (HID_SUCCESS);
789 }
790
791 #endif
792