1 /******************************************************************************
2 *
3 * Copyright (C) 2002-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 file contains the connection interface functions
22 *
23 ******************************************************************************/
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <stdio.h>
28
29
30 #include "bt_common.h"
31 #include "bt_types.h"
32
33 #include "l2cdefs.h"
34 #include "l2c_api.h"
35
36 #include "btu.h"
37 #include "btm_api.h"
38 #include "btm_int.h"
39
40 #include "hiddefs.h"
41
42 #include "hidh_api.h"
43 #include "hidh_int.h"
44 #include "bt_utils.h"
45
46 #include "osi/include/osi.h"
47
48
49 extern fixed_queue_t *btu_general_alarm_queue;
50
51 static UINT8 find_conn_by_cid (UINT16 cid);
52 static void hidh_conn_retry (UINT8 dhandle);
53
54 /********************************************************************************/
55 /* L O C A L F U N C T I O N P R O T O T Y P E S */
56 /********************************************************************************/
57 static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid,
58 UINT16 psm, UINT8 l2cap_id);
59 static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result);
60 static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
61 static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
62 static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
63 static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
64 static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result);
65 static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested);
66
67 static const tL2CAP_APPL_INFO hst_reg_info =
68 {
69 hidh_l2cif_connect_ind,
70 hidh_l2cif_connect_cfm,
71 NULL,
72 hidh_l2cif_config_ind,
73 hidh_l2cif_config_cfm,
74 hidh_l2cif_disconnect_ind,
75 hidh_l2cif_disconnect_cfm,
76 NULL,
77 hidh_l2cif_data_ind,
78 hidh_l2cif_cong_ind,
79 NULL /* tL2CA_TX_COMPLETE_CB */
80 };
81
82 /*******************************************************************************
83 **
84 ** Function hidh_l2cif_reg
85 **
86 ** Description This function initializes the SDP unit.
87 **
88 ** Returns void
89 **
90 *******************************************************************************/
hidh_conn_reg(void)91 tHID_STATUS hidh_conn_reg (void)
92 {
93 int xx;
94
95 /* Initialize the L2CAP configuration. We only care about MTU and flush */
96 memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
97
98 hh_cb.l2cap_cfg.mtu_present = TRUE;
99 hh_cb.l2cap_cfg.mtu = HID_HOST_MTU;
100 hh_cb.l2cap_cfg.flush_to_present = TRUE;
101 hh_cb.l2cap_cfg.flush_to = HID_HOST_FLUSH_TO;
102
103 /* Now, register with L2CAP */
104 if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info))
105 {
106 HIDH_TRACE_ERROR ("HID-Host Control Registration failed");
107 return (HID_ERR_L2CAP_FAILED) ;
108 }
109 if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info))
110 {
111 L2CA_Deregister( HID_PSM_CONTROL ) ;
112 HIDH_TRACE_ERROR ("HID-Host Interrupt Registration failed");
113 return (HID_ERR_L2CAP_FAILED) ;
114 }
115
116 for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++)
117 {
118 hh_cb.devices[xx].in_use = FALSE ;
119 hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED;
120 }
121
122 return (HID_SUCCESS);
123 }
124
125 /*******************************************************************************
126 **
127 ** Function hidh_conn_disconnect
128 **
129 ** Description This function disconnects a connection.
130 **
131 ** Returns TRUE if disconnect started, FALSE if already disconnected
132 **
133 *******************************************************************************/
hidh_conn_disconnect(UINT8 dhandle)134 tHID_STATUS hidh_conn_disconnect (UINT8 dhandle)
135 {
136 tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;
137
138 HIDH_TRACE_EVENT ("HID-Host disconnect");
139
140 if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0))
141 {
142 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;
143
144 /* Set l2cap idle timeout to 0 (so ACL link is disconnected
145 * immediately after last channel is closed) */
146 L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0, BT_TRANSPORT_BR_EDR);
147 /* Disconnect both interrupt and control channels */
148 if (p_hcon->intr_cid)
149 L2CA_DisconnectReq (p_hcon->intr_cid);
150 else if (p_hcon->ctrl_cid)
151 L2CA_DisconnectReq (p_hcon->ctrl_cid);
152 }
153 else
154 {
155 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
156 }
157
158 return (HID_SUCCESS);
159 }
160
161 /*******************************************************************************
162 **
163 ** Function hidh_sec_check_complete_term
164 **
165 ** Description HID security check complete callback function.
166 **
167 ** Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise
168 ** send security block L2C connection response.
169 **
170 *******************************************************************************/
hidh_sec_check_complete_term(BD_ADDR bd_addr,tBT_TRANSPORT transport,void * p_ref_data,UINT8 res)171 void hidh_sec_check_complete_term (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
172 {
173 tHID_HOST_DEV_CTB *p_dev= (tHID_HOST_DEV_CTB *) p_ref_data;
174 UNUSED(bd_addr);
175 UNUSED (transport);
176
177 if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
178 {
179 p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
180
181 p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
182
183 /* Send response to the L2CAP layer. */
184 L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
185
186 /* Send a Configuration Request. */
187 L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg);
188
189 }
190 /* security check fail */
191 else if (res != BTM_SUCCESS)
192 {
193 p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */
194 p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
195 L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK);
196 }
197 }
198
199 /*******************************************************************************
200 **
201 ** Function hidh_l2cif_connect_ind
202 **
203 ** Description This function handles an inbound connection indication
204 ** from L2CAP. This is the case where we are acting as a
205 ** server.
206 **
207 ** Returns void
208 **
209 *******************************************************************************/
hidh_l2cif_connect_ind(BD_ADDR bd_addr,UINT16 l2cap_cid,UINT16 psm,UINT8 l2cap_id)210 static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
211 {
212 tHID_CONN *p_hcon;
213 BOOLEAN bAccept = TRUE;
214 UINT8 i = HID_HOST_MAX_DEVICES;
215 tHID_HOST_DEV_CTB *p_dev;
216
217 HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid);
218
219 /* always add incoming connection device into HID database by default */
220 if (HID_HostAddDev(bd_addr, HID_SEC_REQUIRED, &i) != HID_SUCCESS)
221 {
222 L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0);
223 return;
224 }
225
226 p_hcon = &hh_cb.devices[i].conn;
227 p_dev = &hh_cb.devices[i];
228
229 /* Check we are in the correct state for this */
230 if (psm == HID_PSM_INTERRUPT)
231 {
232 if (p_hcon->ctrl_cid == 0)
233 {
234 HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, but no CTL channel");
235 bAccept = FALSE;
236 }
237 if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)
238 {
239 HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, wrong state: %d",
240 p_hcon->conn_state);
241 bAccept = FALSE;
242 }
243 }
244 else /* CTRL channel */
245 {
246 #if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE)
247 p_hcon->ctrl_cid = p_hcon->intr_cid = 0;
248 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
249 #else
250 if (p_hcon->conn_state != HID_CONN_STATE_UNUSED)
251 {
252 HIDH_TRACE_WARNING ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d",
253 p_hcon->conn_state);
254 bAccept = FALSE;
255 }
256 #endif
257 }
258
259 if (!bAccept)
260 {
261 L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0);
262 return;
263 }
264
265 if (psm == HID_PSM_CONTROL)
266 {
267 p_hcon->conn_flags = 0;
268 p_hcon->ctrl_cid = l2cap_cid;
269 p_hcon->ctrl_id = l2cap_id;
270 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */
271
272 p_hcon->conn_state = HID_CONN_STATE_SECURITY;
273 if(btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL,
274 FALSE, BTM_SEC_PROTO_HID,
275 (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
276 &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED)
277 {
278 L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK);
279 }
280
281 return;
282 }
283
284 /* Transition to the next appropriate state, configuration */
285 p_hcon->conn_state = HID_CONN_STATE_CONFIG;
286 p_hcon->intr_cid = l2cap_cid;
287
288 /* Send response to the L2CAP layer. */
289 L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
290
291 /* Send a Configuration Request. */
292 L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
293
294 HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x",
295 psm, l2cap_cid);
296 }
297
hidh_process_repage_timer_timeout(void * data)298 void hidh_process_repage_timer_timeout(void *data)
299 {
300 uint8_t dhandle = PTR_TO_UINT(data);
301 hidh_try_repage(dhandle);
302 }
303
304 /*******************************************************************************
305 **
306 ** Function hidh_try_repage
307 **
308 ** Description This function processes timeout (to page device).
309 **
310 ** Returns void
311 **
312 *******************************************************************************/
hidh_try_repage(UINT8 dhandle)313 void hidh_try_repage(UINT8 dhandle)
314 {
315 tHID_HOST_DEV_CTB *device;
316
317 hidh_conn_initiate(dhandle);
318
319 device = &hh_cb.devices[dhandle];
320 device->conn_tries++;
321
322 hh_cb.callback(dhandle, device->addr, HID_HDEV_EVT_RETRYING,
323 device->conn_tries, NULL ) ;
324 }
325
326 /*******************************************************************************
327 **
328 ** Function hidh_sec_check_complete_orig
329 **
330 ** Description This function checks to see if security procedures are being
331 ** carried out or not..
332 **
333 ** Returns void
334 **
335 *******************************************************************************/
hidh_sec_check_complete_orig(BD_ADDR bd_addr,tBT_TRANSPORT transport,void * p_ref_data,UINT8 res)336 void hidh_sec_check_complete_orig (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
337 {
338 tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data;
339 UINT8 dhandle;
340 UNUSED(bd_addr);
341 UNUSED (transport);
342
343 // TODO(armansito): This kind of math to determine a device handle is way
344 // too dirty and unnecessary. Why can't |p_dev| store it's handle?
345 dhandle = (PTR_TO_UINT(p_dev) - PTR_TO_UINT(&(hh_cb.devices[0])))/ sizeof(tHID_HOST_DEV_CTB);
346 if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
347 {
348 HIDH_TRACE_EVENT ("HID-Host Originator security pass.");
349 p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
350
351 /* Transition to the next appropriate state, configuration */
352 p_dev->conn.conn_state = HID_CONN_STATE_CONFIG;
353 L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg);
354 HIDH_TRACE_EVENT ("HID-Host Got Control conn cnf, sent cfg req, CID: 0x%x", p_dev->conn.ctrl_cid);
355
356 }
357
358 if( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
359 {
360 #if (HID_HOST_MAX_CONN_RETRY > 0)
361 if( res == BTM_DEVICE_TIMEOUT )
362 {
363 if( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY )
364 {
365 hidh_conn_retry (dhandle);
366 return;
367 }
368 }
369 #endif
370 p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */
371 hidh_conn_disconnect(dhandle);
372 }
373
374 }
375
376 /*******************************************************************************
377 **
378 ** Function hidh_l2cif_connect_cfm
379 **
380 ** Description This function handles the connect confirm events
381 ** from L2CAP. This is the case when we are acting as a
382 ** client and have sent a connect request.
383 **
384 ** Returns void
385 **
386 *******************************************************************************/
hidh_l2cif_connect_cfm(UINT16 l2cap_cid,UINT16 result)387 static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result)
388 {
389 UINT8 dhandle;
390 tHID_CONN *p_hcon = NULL;
391 UINT32 reason;
392 tHID_HOST_DEV_CTB *p_dev = NULL;
393
394 /* Find CCB based on CID, and verify we are in a state to accept this message */
395 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
396 {
397 p_dev = &hh_cb.devices[dhandle];
398 p_hcon = &hh_cb.devices[dhandle].conn;
399 }
400
401 if ((p_hcon == NULL)
402 || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG))
403 || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL))
404 || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)
405 && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING)))
406 {
407 HIDH_TRACE_WARNING ("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid);
408 return;
409 }
410
411 if (result != L2CAP_CONN_OK)
412 {
413 if (l2cap_cid == p_hcon->ctrl_cid)
414 p_hcon->ctrl_cid = 0;
415 else
416 p_hcon->intr_cid = 0;
417
418 hidh_conn_disconnect(dhandle);
419
420 #if (HID_HOST_MAX_CONN_RETRY > 0)
421 if( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) &&
422 (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED ||
423 result == HCI_ERR_PAGE_TIMEOUT) )
424 {
425 hidh_conn_retry(dhandle);
426 }
427 else
428 #endif
429 {
430 reason = HID_L2CAP_CONN_FAIL | (UINT32) result ;
431 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
432 }
433 return;
434 }
435 /* receive Control Channel connect confirmation */
436 if (l2cap_cid == p_hcon->ctrl_cid)
437 {
438 /* check security requirement */
439 p_hcon->conn_state = HID_CONN_STATE_SECURITY;
440 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */
441
442 btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL,
443 TRUE, BTM_SEC_PROTO_HID,
444 (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
445 &hidh_sec_check_complete_orig, p_dev);
446 }
447 else
448 {
449 p_hcon->conn_state = HID_CONN_STATE_CONFIG;
450 /* Send a Configuration Request. */
451 L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
452 HIDH_TRACE_EVENT ("HID-Host got Interrupt conn cnf, sent cfg req, CID: 0x%x", l2cap_cid);
453 }
454
455 return;
456 }
457
458 /*******************************************************************************
459 **
460 ** Function hidh_l2cif_config_ind
461 **
462 ** Description This function processes the L2CAP configuration indication
463 ** event.
464 **
465 ** Returns void
466 **
467 *******************************************************************************/
hidh_l2cif_config_ind(UINT16 l2cap_cid,tL2CAP_CFG_INFO * p_cfg)468 static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
469 {
470 UINT8 dhandle;
471 tHID_CONN *p_hcon = NULL;
472 UINT32 reason;
473
474 /* Find CCB based on CID */
475 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
476 {
477 p_hcon = &hh_cb.devices[dhandle].conn;
478 }
479
480 if (p_hcon == NULL)
481 {
482 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
483 return;
484 }
485
486 HIDH_TRACE_EVENT ("HID-Host Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
487
488 /* Remember the remote MTU size */
489 if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU))
490 p_hcon->rem_mtu_size = HID_HOST_MTU;
491 else
492 p_hcon->rem_mtu_size = p_cfg->mtu;
493
494 /* For now, always accept configuration from the other side */
495 p_cfg->flush_to_present = FALSE;
496 p_cfg->mtu_present = FALSE;
497 p_cfg->result = L2CAP_CFG_OK;
498
499 L2CA_ConfigRsp (l2cap_cid, p_cfg);
500
501 if (l2cap_cid == p_hcon->ctrl_cid)
502 {
503 p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE;
504 if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) &&
505 (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE))
506 {
507 /* Connect interrupt channel */
508 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
509 if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0)
510 {
511 HIDH_TRACE_WARNING ("HID-Host INTR Originate failed");
512 reason = HID_L2CAP_REQ_FAIL ;
513 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
514 hidh_conn_disconnect (dhandle);
515 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
516 return;
517 }
518 else
519 {
520 /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */
521 p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
522 }
523 }
524 }
525 else
526 p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE;
527
528 /* If all configuration is complete, change state and tell management we are up */
529 if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED)
530 && (p_hcon->conn_state == HID_CONN_STATE_CONFIG))
531 {
532 p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
533 /* Reset disconnect reason to success, as connection successful */
534 p_hcon->disc_reason = HID_SUCCESS;
535
536 hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
537 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ;
538 }
539 }
540
541
542 /*******************************************************************************
543 **
544 ** Function hidh_l2cif_config_cfm
545 **
546 ** Description This function processes the L2CAP configuration confirmation
547 ** event.
548 **
549 ** Returns void
550 **
551 *******************************************************************************/
hidh_l2cif_config_cfm(UINT16 l2cap_cid,tL2CAP_CFG_INFO * p_cfg)552 static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
553 {
554 UINT8 dhandle;
555 tHID_CONN *p_hcon = NULL;
556 UINT32 reason;
557
558 HIDH_TRACE_EVENT ("HID-Host Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result);
559
560 /* Find CCB based on CID */
561 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
562 p_hcon = &hh_cb.devices[dhandle].conn;
563
564 if (p_hcon == NULL)
565 {
566 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
567 return;
568 }
569
570 /* If configuration failed, disconnect the channel(s) */
571 if (p_cfg->result != L2CAP_CFG_OK)
572 {
573 hidh_conn_disconnect (dhandle);
574 reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ;
575 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
576 return;
577 }
578
579 if (l2cap_cid == p_hcon->ctrl_cid)
580 {
581 p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE;
582 if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) &&
583 (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE))
584 {
585 /* Connect interrupt channel */
586 p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
587 if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0)
588 {
589 HIDH_TRACE_WARNING ("HID-Host INTR Originate failed");
590 reason = HID_L2CAP_REQ_FAIL ;
591 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
592 hidh_conn_disconnect (dhandle);
593 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
594 return;
595 }
596 else
597 {
598 /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */
599 p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
600 }
601 }
602 }
603 else
604 p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE;
605
606 /* If all configuration is complete, change state and tell management we are up */
607 if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED)
608 && (p_hcon->conn_state == HID_CONN_STATE_CONFIG))
609 {
610 p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
611 /* Reset disconnect reason to success, as connection successful */
612 p_hcon->disc_reason = HID_SUCCESS;
613
614 hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
615 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ;
616 }
617 }
618
619
620 /*******************************************************************************
621 **
622 ** Function hidh_l2cif_disconnect_ind
623 **
624 ** Description This function handles a disconnect event from L2CAP. If
625 ** requested to, we ack the disconnect before dropping the CCB
626 **
627 ** Returns void
628 **
629 *******************************************************************************/
hidh_l2cif_disconnect_ind(UINT16 l2cap_cid,BOOLEAN ack_needed)630 static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
631 {
632 UINT8 dhandle;
633 tHID_CONN *p_hcon = NULL;
634 UINT16 disc_res = HCI_SUCCESS;
635 UINT16 hid_close_evt_reason;
636
637 /* Find CCB based on CID */
638 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
639 p_hcon = &hh_cb.devices[dhandle].conn;
640
641 if (p_hcon == NULL)
642 {
643 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
644 return;
645 }
646
647 if (ack_needed)
648 L2CA_DisconnectRsp (l2cap_cid);
649
650 HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
651
652 p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;
653
654 if (l2cap_cid == p_hcon->ctrl_cid)
655 p_hcon->ctrl_cid = 0;
656 else
657 p_hcon->intr_cid = 0;
658
659 if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0))
660 {
661 hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
662 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
663
664 if( !ack_needed )
665 disc_res = btm_get_acl_disc_reason_code();
666
667 #if (HID_HOST_MAX_CONN_RETRY > 0)
668 if( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) &&
669 (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) &&
670 (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE))
671 {
672 hh_cb.devices[dhandle].conn_tries = 0;
673 period_ms_t interval_ms = HID_HOST_REPAGE_WIN * 1000;
674 alarm_set_on_queue(hh_cb.devices[dhandle].conn.process_repage_timer,
675 interval_ms, hidh_process_repage_timer_timeout,
676 UINT_TO_PTR(dhandle), btu_general_alarm_queue);
677 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, disc_res, NULL);
678 }
679 else
680 #endif
681 {
682 /* Set reason code for HID_HDEV_EVT_CLOSE */
683 hid_close_evt_reason = p_hcon->disc_reason;
684
685 /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */
686 if ((disc_res == HCI_ERR_AUTH_FAILURE) ||
687 (disc_res == HCI_ERR_KEY_MISSING) ||
688 (disc_res == HCI_ERR_HOST_REJECT_SECURITY) ||
689 (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED) ||
690 (disc_res == HCI_ERR_UNIT_KEY_USED) ||
691 (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) ||
692 (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) ||
693 (disc_res == HCI_ERR_REPEATED_ATTEMPTS))
694 {
695 hid_close_evt_reason = HID_ERR_AUTH_FAILED;
696 }
697
698 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ;
699 }
700 }
701 }
702
703
704 /*******************************************************************************
705 **
706 ** Function hidh_l2cif_disconnect_cfm
707 **
708 ** Description This function handles a disconnect confirm event from L2CAP.
709 **
710 ** Returns void
711 **
712 *******************************************************************************/
hidh_l2cif_disconnect_cfm(UINT16 l2cap_cid,UINT16 result)713 static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
714 {
715 UINT8 dhandle;
716 tHID_CONN *p_hcon = NULL;
717 UNUSED(result);
718
719 /* Find CCB based on CID */
720 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
721 p_hcon = &hh_cb.devices[dhandle].conn;
722
723 if (p_hcon == NULL)
724 {
725 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
726 return;
727 }
728
729 HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
730
731 if (l2cap_cid == p_hcon->ctrl_cid)
732 p_hcon->ctrl_cid = 0;
733 else
734 {
735 p_hcon->intr_cid = 0;
736 if (p_hcon->ctrl_cid)
737 {
738 HIDH_TRACE_EVENT ("HID-Host Initiating L2CAP Ctrl disconnection");
739 L2CA_DisconnectReq (p_hcon->ctrl_cid);
740 }
741 }
742
743 if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0))
744 {
745 hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
746 p_hcon->conn_state = HID_CONN_STATE_UNUSED;
747 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ;
748 }
749 }
750
751
752 /*******************************************************************************
753 **
754 ** Function hidh_l2cif_cong_ind
755 **
756 ** Description This function handles a congestion status event from L2CAP.
757 **
758 ** Returns void
759 **
760 *******************************************************************************/
hidh_l2cif_cong_ind(UINT16 l2cap_cid,BOOLEAN congested)761 static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested)
762 {
763 UINT8 dhandle;
764 tHID_CONN *p_hcon = NULL;
765
766 /* Find CCB based on CID */
767 if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
768 p_hcon = &hh_cb.devices[dhandle].conn;
769
770 if (p_hcon == NULL)
771 {
772 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid);
773 return;
774 }
775
776 HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested);
777
778 if (congested)
779 p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED;
780 else
781 {
782 p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED;
783
784 }
785 }
786
787
788 /*******************************************************************************
789 **
790 ** Function hidh_l2cif_data_ind
791 **
792 ** Description This function is called when data is received from L2CAP.
793 ** if we are the originator of the connection, we are the SDP
794 ** client, and the received message is queued up for the client.
795 **
796 ** If we are the destination of the connection, we are the SDP
797 ** server, so the message is passed to the server processing
798 ** function.
799 **
800 ** Returns void
801 **
802 *******************************************************************************/
hidh_l2cif_data_ind(UINT16 l2cap_cid,BT_HDR * p_msg)803 static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
804 {
805 UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset;
806 UINT8 ttype, param, rep_type, evt;
807 UINT8 dhandle;
808 tHID_CONN *p_hcon = NULL;
809
810 HIDH_TRACE_DEBUG ("HID-Host hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid);
811
812 /* Find CCB based on CID */
813 if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES)
814 p_hcon = &hh_cb.devices[dhandle].conn;
815
816 if (p_hcon == NULL)
817 {
818 HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
819 osi_free(p_msg);
820 return;
821 }
822
823
824 ttype = HID_GET_TRANS_FROM_HDR(*p_data);
825 param = HID_GET_PARAM_FROM_HDR(*p_data);
826 rep_type = param & HID_PAR_REP_TYPE_MASK;
827 p_data++;
828
829 /* Get rid of the data type */
830 p_msg->len--;
831 p_msg->offset++;
832
833 switch (ttype)
834 {
835 case HID_TRANS_HANDSHAKE:
836 hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_HANDSHAKE, param, NULL);
837 osi_free(p_msg);
838 break;
839
840 case HID_TRANS_CONTROL:
841 switch (param)
842 {
843 case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG:
844 hidh_conn_disconnect( dhandle ) ;
845 /* Device is unplugging from us. Tell USB */
846 hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_VC_UNPLUG, 0, NULL);
847 break;
848
849 default:
850 break;
851 }
852 osi_free(p_msg);
853 break;
854
855
856 case HID_TRANS_DATA:
857 evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
858 HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA;
859 hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg);
860 break;
861
862 case HID_TRANS_DATAC:
863 evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
864 HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC;
865 hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg);
866 break;
867
868 default:
869 osi_free(p_msg);
870 break;
871 }
872 }
873
874 /*******************************************************************************
875 **
876 ** Function hidh_conn_snd_data
877 **
878 ** Description This function is sends out data.
879 **
880 ** Returns tHID_STATUS
881 **
882 *******************************************************************************/
hidh_conn_snd_data(UINT8 dhandle,UINT8 trans_type,UINT8 param,UINT16 data,UINT8 report_id,BT_HDR * buf)883 tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param,
884 UINT16 data, UINT8 report_id, BT_HDR *buf)
885 {
886 tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;
887 BT_HDR *p_buf;
888 UINT8 *p_out;
889 UINT16 bytes_copied;
890 BOOLEAN seg_req = FALSE;
891 UINT16 data_size;
892 UINT16 cid;
893 UINT16 buf_size;
894 UINT8 use_data = 0 ;
895 BOOLEAN blank_datc = FALSE;
896
897 if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr, BT_TRANSPORT_BR_EDR))
898 {
899 osi_free(buf);
900 return HID_ERR_NO_CONNECTION;
901 }
902
903 if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED)
904 {
905 osi_free(buf);
906 return HID_ERR_CONGESTED;
907 }
908
909 switch( trans_type )
910 {
911 case HID_TRANS_CONTROL:
912 case HID_TRANS_GET_REPORT:
913 case HID_TRANS_SET_REPORT:
914 case HID_TRANS_GET_PROTOCOL:
915 case HID_TRANS_SET_PROTOCOL:
916 case HID_TRANS_GET_IDLE:
917 case HID_TRANS_SET_IDLE:
918 cid = p_hcon->ctrl_cid;
919 buf_size = HID_CONTROL_BUF_SIZE;
920 break;
921 case HID_TRANS_DATA:
922 cid = p_hcon->intr_cid;
923 buf_size = HID_INTERRUPT_BUF_SIZE;
924 break;
925 default:
926 return (HID_ERR_INVALID_PARAM) ;
927 }
928
929 if( trans_type == HID_TRANS_SET_IDLE )
930 use_data = 1;
931 else if( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) )
932 use_data = 2;
933
934 do
935 {
936 if ( buf == NULL || blank_datc )
937 {
938 p_buf = (BT_HDR *)osi_malloc(buf_size);
939
940 p_buf->offset = L2CAP_MIN_OFFSET;
941 seg_req = FALSE;
942 data_size = 0;
943 bytes_copied = 0;
944 blank_datc = FALSE;
945 }
946 else if ( (buf->len > (p_hcon->rem_mtu_size - 1)))
947 {
948 p_buf = (BT_HDR *)osi_malloc(buf_size);
949
950 p_buf->offset = L2CAP_MIN_OFFSET;
951 seg_req = TRUE;
952 data_size = buf->len;
953 bytes_copied = p_hcon->rem_mtu_size - 1;
954 }
955 else
956 {
957 p_buf = buf ;
958 p_buf->offset -= 1;
959 seg_req = FALSE;
960 data_size = buf->len;
961 bytes_copied = buf->len;
962 }
963
964 p_out = (UINT8 *)(p_buf + 1) + p_buf->offset;
965 *p_out++ = HID_BUILD_HDR(trans_type, param);
966
967 /* If report ID required for this device */
968 if( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) )
969 {
970 *p_out = report_id;
971 data_size = bytes_copied = 1;
972 }
973
974
975 if (seg_req)
976 {
977 memcpy (p_out, (((UINT8 *)(buf+1)) + buf->offset), bytes_copied);
978 buf->offset += bytes_copied;
979 buf->len -= bytes_copied;
980 }
981 else if( use_data == 1)
982 {
983 *(p_out+bytes_copied) = data & 0xff;
984 }
985 else if( use_data == 2 )
986 {
987 *(p_out+bytes_copied) = data & 0xff;
988 *(p_out+bytes_copied+1) = (data >> 8) & 0xff ;
989 }
990
991 p_buf->len = bytes_copied + 1 + use_data;
992 data_size -= bytes_copied;
993
994 /* Send the buffer through L2CAP */
995 if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (!L2CA_DataWrite (cid, p_buf)))
996 return (HID_ERR_CONGESTED);
997
998 if (data_size)
999 trans_type = HID_TRANS_DATAC;
1000 else if( bytes_copied == (p_hcon->rem_mtu_size - 1) )
1001 {
1002 trans_type = HID_TRANS_DATAC;
1003 blank_datc = TRUE;
1004 }
1005
1006 } while ((data_size != 0) || blank_datc ) ;
1007
1008 return (HID_SUCCESS);
1009 }
1010 /*******************************************************************************
1011 **
1012 ** Function hidh_conn_initiate
1013 **
1014 ** Description This function is called by the management to create a connection.
1015 **
1016 ** Returns void
1017 **
1018 *******************************************************************************/
hidh_conn_initiate(UINT8 dhandle)1019 tHID_STATUS hidh_conn_initiate (UINT8 dhandle)
1020 {
1021 UINT8 service_id = BTM_SEC_SERVICE_HIDH_NOSEC_CTRL;
1022 UINT32 mx_chan_id = HID_NOSEC_CHN;
1023
1024 tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
1025
1026 if( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED )
1027 return( HID_ERR_CONN_IN_PROCESS );
1028
1029 p_dev->conn.ctrl_cid = 0;
1030 p_dev->conn.intr_cid = 0;
1031 p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
1032
1033 /* We are the originator of this connection */
1034 p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;
1035
1036 if(p_dev->attr_mask & HID_SEC_REQUIRED)
1037 {
1038 service_id = BTM_SEC_SERVICE_HIDH_SEC_CTRL;
1039 mx_chan_id = HID_SEC_CHN;
1040 }
1041 BTM_SetOutService (p_dev->addr, service_id, mx_chan_id);
1042
1043 /* Check if L2CAP started the connection process */
1044 if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0)
1045 {
1046 HIDH_TRACE_WARNING ("HID-Host Originate failed");
1047 hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE,
1048 HID_ERR_L2CAP_FAILED, NULL ) ;
1049 }
1050 else
1051 {
1052 /* Transition to the next appropriate state, waiting for connection confirm on control channel. */
1053 p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;
1054 }
1055
1056 return( HID_SUCCESS );
1057 }
1058
1059
1060 /*******************************************************************************
1061 **
1062 ** Function find_conn_by_cid
1063 **
1064 ** Description This function finds a connection control block based on CID
1065 **
1066 ** Returns address of control block, or NULL if not found
1067 **
1068 *******************************************************************************/
find_conn_by_cid(UINT16 cid)1069 static UINT8 find_conn_by_cid (UINT16 cid)
1070 {
1071 UINT8 xx;
1072
1073 for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++)
1074 {
1075 if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED)
1076 && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid)))
1077 break;
1078 }
1079
1080 return (xx);
1081 }
1082
hidh_conn_dereg(void)1083 void hidh_conn_dereg( void )
1084 {
1085 L2CA_Deregister (HID_PSM_CONTROL);
1086 L2CA_Deregister (HID_PSM_INTERRUPT);
1087 }
1088
1089 /*******************************************************************************
1090 **
1091 ** Function hidh_conn_retry
1092 **
1093 ** Description This function is called to retry a failed connection.
1094 **
1095 ** Returns void
1096 **
1097 *******************************************************************************/
hidh_conn_retry(UINT8 dhandle)1098 static void hidh_conn_retry( UINT8 dhandle )
1099 {
1100 tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
1101
1102 p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
1103 #if (HID_HOST_REPAGE_WIN > 0)
1104 period_ms_t interval_ms = HID_HOST_REPAGE_WIN * 1000;
1105 alarm_set_on_queue(p_dev->conn.process_repage_timer,
1106 interval_ms, hidh_process_repage_timer_timeout,
1107 UINT_TO_PTR(dhandle), btu_general_alarm_queue);
1108 #else
1109 hidh_process_repage_process(dhandle);
1110 #endif
1111 }
1112