/******************************************************************************* ** ** Function avdt_ccb_chk_timer ** ** Description This function stops the CCB timer if the idle timer is ** running. ** ** ** Returns void. ** *******************************************************************************/ void avdt_ccb_chk_timer(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data) { if (p_ccb->timer_entry.event == BTU_TTYPE_AVDT_CCB_IDLE) { btu_stop_timer(&p_ccb->timer_entry); } }
/******************************************************************************* ** ** Function l2c_process_held_packets ** ** Description This function processes any L2CAP packets that arrived before ** the HCI connection complete arrived. It is a work around for ** badly behaved controllers. ** ** Returns void ** *******************************************************************************/ void l2c_process_held_packets (BOOLEAN timed_out) { BT_HDR *p_buf, *p_buf1; BUFFER_Q *p_rcv_hold_q = &l2cb.rcv_hold_q; if (!p_rcv_hold_q->count) return; if (!timed_out) { btu_stop_timer(&l2cb.rcv_hold_tle); L2CAP_TRACE_WARNING0("L2CAP HOLD CONTINUE"); } else { L2CAP_TRACE_WARNING0("L2CAP HOLD TIMEOUT"); } /* Update the timeouts in the hold queue */ for (p_buf = (BT_HDR *)GKI_getfirst (p_rcv_hold_q); p_buf; p_buf = p_buf1) { p_buf1 = (BT_HDR *)GKI_getnext (p_buf); if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0)) { GKI_remove_from_queue (p_rcv_hold_q, p_buf); p_buf->layer_specific = 0xFFFF; l2c_rcv_acl_data (p_buf); } } /* If anyone still in the queue, restart the timeout */ if (p_rcv_hold_q->count) btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); }
/******************************************************************************* ** ** Function l2c_process_held_packets ** ** Description This function processes any L2CAP packets that arrived before ** the HCI connection complete arrived. It is a work around for ** badly behaved controllers. ** ** Returns void ** *******************************************************************************/ void l2c_process_held_packets(BOOLEAN timed_out) { if (list_is_empty(l2cb.rcv_pending_q)) { return; } if (!timed_out) { btu_stop_timer(&l2cb.rcv_hold_tle); L2CAP_TRACE_WARNING("L2CAP HOLD CONTINUE"); } else { L2CAP_TRACE_WARNING("L2CAP HOLD TIMEOUT"); } for (const list_node_t *node = list_begin(l2cb.rcv_pending_q); node != list_end(l2cb.rcv_pending_q);) { BT_HDR *p_buf = list_node(node); node = list_next(node); if (!timed_out || (!p_buf->layer_specific) || (--p_buf->layer_specific == 0)) { list_remove(l2cb.rcv_pending_q, p_buf); p_buf->layer_specific = 0xFFFF; l2c_rcv_acl_data(p_buf); } } /* If anyone still in the queue, restart the timeout */ if (!list_is_empty(l2cb.rcv_pending_q)) { btu_start_timer (&l2cb.rcv_hold_tle, BTU_TTYPE_L2CAP_HOLD, BT_1SEC_TIMEOUT); } }
/******************************************************************************* ** ** Function bta_pan_scb_dealloc ** ** Description Deallocate a link control block. ** ** ** Returns void ** *******************************************************************************/ void bta_pan_scb_dealloc(tBTA_PAN_SCB *p_scb) { APPL_TRACE_DEBUG("bta_pan_scb_dealloc %d", bta_pan_scb_to_idx(p_scb)); if (p_scb->is_idle_timer_started == TRUE) { /* Ensure that timer is stopped */ btu_stop_timer (&p_scb->idle_tle); } memset(p_scb, 0, sizeof(tBTA_PAN_SCB)); }
/******************************************************************************* ** ** Function avdt_scb_dealloc ** ** Description Deallocate a stream control block. ** ** ** Returns void. ** *******************************************************************************/ void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { UNUSED(p_data); AVDT_TRACE_DEBUG("avdt_scb_dealloc hdl=%d\n", avdt_scb_to_hdl(p_scb)); btu_stop_timer(&p_scb->timer_entry); #if AVDT_MULTIPLEXING == TRUE /* free fragments we're holding, if any; it shouldn't happen */ fixed_queue_free(p_scb->frag_q, osi_free_func); #endif memset(p_scb, 0, sizeof(tAVDT_SCB)); }
void btu_deregister_timer(TIMER_LIST_ENT *p_tle) { UINT8 i = 0; for (; i < BTU_MAX_REG_TIMER; i++) { if (btu_cb.timer_reg[i].p_tle == p_tle) { btu_stop_timer(p_tle); btu_cb.timer_reg[i].timer_cb = NULL; btu_cb.timer_reg[i].p_tle = NULL; break; } } }
/******************************************************************************* ** ** Function smp_br_data_received ** ** Description This function is called when data is received from L2CAP on ** SMP BR channel. ** ** Returns void ** *******************************************************************************/ static void smp_br_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) { tSMP_CB *p_cb = &smp_cb; UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; UINT8 cmd ; SMP_TRACE_EVENT ("SMDBG l2c %s", __func__); STREAM_TO_UINT8(cmd, p); /* sanity check */ if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) { SMP_TRACE_WARNING( "Ignore received command with RESERVED code 0x%02x", cmd); GKI_freebuf(p_buf); return; } /* reject the pairing request if there is an on-going SMP pairing */ if (SMP_OPCODE_PAIRING_REQ == cmd) { if ((p_cb->state == SMP_STATE_IDLE) && (p_cb->br_state == SMP_BR_STATE_IDLE)) { p_cb->role = HCI_ROLE_SLAVE; p_cb->smp_over_br = TRUE; memcpy(&p_cb->pairing_bda[0], bd_addr, BD_ADDR_LEN); } else if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN)) { GKI_freebuf (p_buf); smp_reject_unexpected_pairing_command(bd_addr); return; } /* else, out of state pairing request received, passed into State Machine */ } if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN) == 0) { btu_stop_timer (&p_cb->rsp_timer_ent); btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, SMP_WAIT_FOR_RSP_TOUT); p_cb->rcvd_cmd_code = cmd; p_cb->rcvd_cmd_len = (UINT8) p_buf->len; smp_br_state_machine_event(p_cb, cmd, p); } GKI_freebuf (p_buf); }
/******************************************************************************* ** ** Function HID_HostCloseDev ** ** Description This function disconnects the device. ** ** Returns void ** *******************************************************************************/ tHID_STATUS HID_HostCloseDev( UINT8 dev_handle ) { if( !hh_cb.reg_flag ) return (HID_ERR_NOT_REGISTERED); if( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) return HID_ERR_INVALID_PARAM; hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ; if( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) return HID_ERR_NO_CONNECTION; hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY+1; return hidh_conn_disconnect( dev_handle ); }
/******************************************************************************* ** ** Function avdt_scb_dealloc ** ** Description Deallocate a stream control block. ** ** ** Returns void. ** *******************************************************************************/ void avdt_scb_dealloc(tAVDT_SCB *p_scb, tAVDT_SCB_EVT *p_data) { #if AVDT_MULTIPLEXING == TRUE void *p_buf; #endif UNUSED(p_data); AVDT_TRACE_DEBUG("avdt_scb_dealloc hdl=%d", avdt_scb_to_hdl(p_scb)); btu_stop_timer(&p_scb->timer_entry); #if AVDT_MULTIPLEXING == TRUE /* free fragments we're holding, if any; it shouldn't happen */ while ((p_buf = GKI_dequeue (&p_scb->frag_q)) != NULL) GKI_freebuf(p_buf); #endif memset(p_scb, 0, sizeof(tAVDT_SCB)); }
/******************************************************************************* ** ** Function btm_read_local_name_complete ** ** Description This function is called when local name read complete. ** message is received from the HCI. ** ** Returns void ** *******************************************************************************/ void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len) { tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; UINT8 status; UNUSED(evt_len); btu_stop_timer (&btm_cb.devcb.rln_timer); /* If there was a callback address for read local name, call it */ btm_cb.devcb.p_rln_cmpl_cb = NULL; if (p_cb) { STREAM_TO_UINT8 (status, p); if (status == HCI_SUCCESS) (*p_cb)(p); else (*p_cb)(NULL); } }
/******************************************************************************* ** ** Function l2cble_scanner_conn_comp ** ** Description This function is called when an HCI Connection Complete ** event is received while we are a scanner (so we are master). ** ** Returns void ** *******************************************************************************/ void l2cble_scanner_conn_comp (UINT16 handle, BD_ADDR bda, tBLE_ADDR_TYPE type, UINT16 conn_interval, UINT16 conn_latency, UINT16 conn_timeout) { tL2C_LCB *p_lcb; tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bda); L2CAP_TRACE_DEBUG5 ("l2cble_scanner_conn_comp: HANDLE=%d addr_type=%d conn_interval=%d slave_latency=%d supervision_tout=%d", handle, type, conn_interval, conn_latency, conn_timeout); l2cb.is_ble_connecting = FALSE; /* See if we have a link control block for the remote device */ p_lcb = l2cu_find_lcb_by_bd_addr (bda); /* If we don't have one, create one. this is auto connection complete. */ if (!p_lcb) { p_lcb = l2cu_allocate_lcb (bda, FALSE); if (!p_lcb) { btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); L2CAP_TRACE_ERROR0 ("l2cble_scanner_conn_comp - failed to allocate LCB"); return; } else { if (!l2cu_initialize_fixed_ccb (p_lcb, L2CAP_ATT_CID, &l2cb.fixed_reg[L2CAP_ATT_CID - L2CAP_FIRST_FIXED_CHNL].fixed_chnl_opts)) { btm_sec_disconnect (handle, HCI_ERR_NO_CONNECTION); L2CAP_TRACE_WARNING0 ("l2cble_scanner_conn_comp - LCB but no CCB"); return ; } } } else if (p_lcb->link_state != LST_CONNECTING) { L2CAP_TRACE_ERROR1 ("L2CAP got BLE scanner conn_comp in bad state: %d", p_lcb->link_state); return; } btu_stop_timer(&p_lcb->timer_entry); /* Save the handle */ p_lcb->handle = handle; /* Connected OK. Change state to connected, we were scanning so we are master */ p_lcb->link_state = LST_CONNECTED; p_lcb->link_role = HCI_ROLE_MASTER; p_lcb->is_ble_link = TRUE; /* If there are any preferred connection parameters, set them now */ if ( (p_dev_rec->conn_params.min_conn_int >= BTM_BLE_CONN_INT_MIN ) && (p_dev_rec->conn_params.min_conn_int <= BTM_BLE_CONN_INT_MAX ) && (p_dev_rec->conn_params.max_conn_int >= BTM_BLE_CONN_INT_MIN ) && (p_dev_rec->conn_params.max_conn_int <= BTM_BLE_CONN_INT_MAX ) && (p_dev_rec->conn_params.slave_latency <= BTM_BLE_CONN_LATENCY_MAX ) && (p_dev_rec->conn_params.supervision_tout >= BTM_BLE_CONN_SUP_TOUT_MIN) && (p_dev_rec->conn_params.supervision_tout <= BTM_BLE_CONN_SUP_TOUT_MAX) && ((conn_interval < p_dev_rec->conn_params.min_conn_int && p_dev_rec->conn_params.min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) || (conn_interval > p_dev_rec->conn_params.max_conn_int) || (conn_latency > p_dev_rec->conn_params.slave_latency) || (conn_timeout > p_dev_rec->conn_params.supervision_tout))) { L2CAP_TRACE_ERROR5 ("upd_ll_conn_params: HANDLE=%d min_conn_int=%d max_conn_int=%d slave_latency=%d supervision_tout=%d", handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout); btsnd_hcic_ble_upd_ll_conn_params (handle, p_dev_rec->conn_params.min_conn_int, p_dev_rec->conn_params.max_conn_int, p_dev_rec->conn_params.slave_latency, p_dev_rec->conn_params.supervision_tout, 0, 0); } /* Tell BTM Acl management about the link */ btm_acl_created (bda, NULL, p_dev_rec->sec_bd_name, handle, p_lcb->link_role, TRUE); if (p_lcb->p_echo_rsp_cb) { L2CAP_TRACE_ERROR0 ("l2cu_send_peer_echo_req"); l2cu_send_peer_echo_req (p_lcb, NULL, 0); } p_lcb->peer_chnl_mask[0] = L2CAP_FIXED_CHNL_ATT_BIT | L2CAP_FIXED_CHNL_BLE_SIG_BIT | L2CAP_FIXED_CHNL_SMP_BIT; l2cu_process_fixed_chnl_resp (p_lcb); }
/******************************************************************************* ** ** Function hidh_l2cif_connect_ind ** ** Description This function handles an inbound connection indication ** from L2CAP. This is the case where we are acting as a ** server. ** ** Returns void ** *******************************************************************************/ static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id) { tHID_CONN *p_hcon; BOOLEAN bAccept = TRUE; UINT8 i = HID_HOST_MAX_DEVICES; tHID_HOST_DEV_CTB *p_dev; HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); /* always add incoming connection device into HID database by default */ if (HID_HostAddDev(bd_addr, HID_SEC_REQUIRED, &i) != HID_SUCCESS) { L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0); return; } p_hcon = &hh_cb.devices[i].conn; p_dev = &hh_cb.devices[i]; /* Stop the retry timer if active */ #if (HID_HOST_MAX_CONN_RETRY > 0) #if (HID_HOST_REPAGE_WIN > 0) HIDH_TRACE_DEBUG ("HID-Host stopping retry timer as l2cap is connected from remote side"); p_dev->conn_tries = HID_HOST_MAX_CONN_RETRY+1; btu_stop_timer(&(p_dev->conn.timer_entry)); #endif #endif /* Check we are in the correct state for this */ if (psm == HID_PSM_INTERRUPT) { if (p_hcon->ctrl_cid == 0) { HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, but no CTL channel"); bAccept = FALSE; } if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state); bAccept = FALSE; } } else /* CTRL channel */ { #if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE) p_hcon->ctrl_cid = p_hcon->intr_cid = 0; p_hcon->conn_state = HID_CONN_STATE_UNUSED; #else if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { HIDH_TRACE_WARNING ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d", p_hcon->conn_state); bAccept = FALSE; } #endif } if (!bAccept) { L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0); return; } if (psm == HID_PSM_CONTROL) { p_hcon->conn_flags = 0; p_hcon->ctrl_cid = l2cap_cid; p_hcon->ctrl_id = l2cap_id; 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' */ p_hcon->conn_state = HID_CONN_STATE_SECURITY; if (!interop_addr_match(INTEROP_DISABLE_AUTH_FOR_HID_POINTING, (bt_bdaddr_t*)p_dev->addr)) { if(btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN, &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED) { L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); } } else { /* device is blacklisted, don't perform authentication */ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); hidh_sec_check_complete_term(p_dev->addr, BT_TRANSPORT_BR_EDR, p_dev, BTM_SUCCESS); } return; } /* Transition to the next appropriate state, configuration */ p_hcon->conn_state = HID_CONN_STATE_CONFIG; p_hcon->intr_cid = l2cap_cid; /* Send response to the L2CAP layer. */ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); /* Send a Configuration Request. */ L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg); HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x", psm, l2cap_cid); }
/******************************************************************************* ** ** Function rfc_timer_stop ** ** Description Stop RFC Timer ** *******************************************************************************/ void rfc_timer_stop (tRFC_MCB *p_mcb) { RFCOMM_TRACE_EVENT ("rfc_timer_stop"); btu_stop_timer (&p_mcb->tle); }
/******************************************************************************* ** ** Function process_l2cap_cmd ** ** Description This function is called when a packet is received on the ** L2CAP signalling CID ** ** Returns void ** *******************************************************************************/ static void process_l2cap_cmd (tL2C_LCB *p_lcb, UINT8 *p, UINT16 pkt_len) { UINT8 *p_pkt_end, *p_next_cmd, *p_cfg_end, *p_cfg_start; UINT8 cmd_code, cfg_code, cfg_len, id; tL2C_CONN_INFO con_info; tL2CAP_CFG_INFO cfg_info; UINT16 rej_reason, rej_mtu, lcid, rcid, info_type; tL2C_CCB *p_ccb; tL2C_RCB *p_rcb; BOOLEAN cfg_rej; UINT16 cfg_rej_len, cmd_len; UINT16 result; tL2C_CONN_INFO ci; #if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) /* if l2cap command received in CID 1 on top of an LE link, ignore this command */ if (p_lcb->is_ble_link) return; #endif p_next_cmd = p; p_pkt_end = p + pkt_len; memset (&cfg_info, 0, sizeof(cfg_info)); /* An L2CAP packet may contain multiple commands */ while (TRUE) { /* Smallest command is 4 bytes */ if ((p = p_next_cmd) > (p_pkt_end - 4)) break; STREAM_TO_UINT8 (cmd_code, p); STREAM_TO_UINT8 (id, p); STREAM_TO_UINT16 (cmd_len, p); if(cmd_len > GKI_BUF2_SIZE) { L2CAP_TRACE_WARNING0 ("L2CAP - Invalid MTU Size"); l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_MTU_EXCEEDED, id, 0, 0); return; } /* Check command length does not exceed packet length */ if ((p_next_cmd = p + cmd_len) > p_pkt_end) { L2CAP_TRACE_WARNING3 ("Command len bad pkt_len: %d cmd_len: %d code: %d", pkt_len, cmd_len, cmd_code); break; } switch (cmd_code) { case L2CAP_CMD_REJECT: STREAM_TO_UINT16 (rej_reason, p); if (rej_reason == L2CAP_CMD_REJ_MTU_EXCEEDED) { STREAM_TO_UINT16 (rej_mtu, p); /* What to do with the MTU reject ? We have negotiated an MTU. For now */ /* we will ignore it and let a higher protocol timeout take care of it */ L2CAP_TRACE_WARNING2 ("L2CAP - MTU rej Handle: %d MTU: %d", p_lcb->handle, rej_mtu); } if (rej_reason == L2CAP_CMD_REJ_INVALID_CID) { STREAM_TO_UINT16 (rcid, p); STREAM_TO_UINT16 (lcid, p); L2CAP_TRACE_WARNING2 ("L2CAP - rej with CID invalid, LCID: 0x%04x RCID: 0x%04x", lcid, rcid); /* Remote CID invalid. Treat as a disconnect */ if (((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) && (p_ccb->remote_cid == rcid)) { /* Fake link disconnect - no reply is generated */ l2c_csm_execute (p_ccb, L2CEVT_LP_DISCONNECT_IND, NULL); } } /* SonyEricsson Info request Bug workaround (Continue connection) */ else if (rej_reason == L2CAP_CMD_REJ_NOT_UNDERSTOOD && p_lcb->w4_info_rsp) { btu_stop_timer (&p_lcb->info_timer_entry); p_lcb->w4_info_rsp = FALSE; ci.status = HCI_SUCCESS; memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); /* For all channels, send the event through their FSMs */ for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); } } break; case L2CAP_CMD_CONN_REQ: STREAM_TO_UINT16 (con_info.psm, p); STREAM_TO_UINT16 (rcid, p); if ((p_rcb = l2cu_find_rcb_by_psm (con_info.psm)) == NULL) { L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for unknown PSM: %d", con_info.psm); l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); break; } else { if (!p_rcb->api.pL2CA_ConnectInd_Cb) { L2CAP_TRACE_WARNING1 ("L2CAP - rcvd conn req for outgoing-only connection PSM: %d", con_info.psm); l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_PSM); break; } } if ((p_ccb = l2cu_allocate_ccb (p_lcb, 0)) == NULL) { L2CAP_TRACE_ERROR0 ("L2CAP - unable to allocate CCB"); l2cu_reject_connection (p_lcb, rcid, id, L2CAP_CONN_NO_RESOURCES); break; } p_ccb->remote_id = id; p_ccb->p_rcb = p_rcb; p_ccb->remote_cid = rcid; l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_REQ, &con_info); break; case L2CAP_CMD_CONN_RSP: STREAM_TO_UINT16 (con_info.remote_cid, p); STREAM_TO_UINT16 (lcid, p); STREAM_TO_UINT16 (con_info.l2cap_result, p); STREAM_TO_UINT16 (con_info.l2cap_status, p); if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) == NULL) { L2CAP_TRACE_WARNING2 ("L2CAP - no CCB for conn rsp, LCID: %d RCID: %d", lcid, con_info.remote_cid); break; } if (p_ccb->local_id != id) { L2CAP_TRACE_WARNING2 ("L2CAP - con rsp - bad ID. Exp: %d Got: %d", p_ccb->local_id, id); break; } if (con_info.l2cap_result == L2CAP_CONN_OK) l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP, &con_info); else if (con_info.l2cap_result == L2CAP_CONN_PENDING) l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_PND, &con_info); else l2c_csm_execute(p_ccb, L2CEVT_L2CAP_CONNECT_RSP_NEG, &con_info); break; case L2CAP_CMD_CONFIG_REQ: p_cfg_end = p + cmd_len; cfg_rej = FALSE; cfg_rej_len = 0; STREAM_TO_UINT16 (lcid, p); STREAM_TO_UINT16 (cfg_info.flags, p); p_cfg_start = p; cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = cfg_info.fcr_present = cfg_info.fcs_present = FALSE; while (p < p_cfg_end) { STREAM_TO_UINT8 (cfg_code, p); STREAM_TO_UINT8 (cfg_len, p); switch (cfg_code & 0x7F) { case L2CAP_CFG_TYPE_MTU: cfg_info.mtu_present = TRUE; STREAM_TO_UINT16 (cfg_info.mtu, p); break; case L2CAP_CFG_TYPE_FLUSH_TOUT: cfg_info.flush_to_present = TRUE; STREAM_TO_UINT16 (cfg_info.flush_to, p); break; case L2CAP_CFG_TYPE_QOS: cfg_info.qos_present = TRUE; STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); STREAM_TO_UINT8 (cfg_info.qos.service_type, p); STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); STREAM_TO_UINT32 (cfg_info.qos.latency, p); STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); break; case L2CAP_CFG_TYPE_FCR: cfg_info.fcr_present = TRUE; STREAM_TO_UINT8 (cfg_info.fcr.mode, p); STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); STREAM_TO_UINT16 (cfg_info.fcr.mps, p); break; case L2CAP_CFG_TYPE_FCS: cfg_info.fcs_present = TRUE; STREAM_TO_UINT8 (cfg_info.fcs, p); break; case L2CAP_CFG_TYPE_EXT_FLOW: cfg_info.ext_flow_spec_present = TRUE; STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); break; default: /* sanity check option length */ if ((cfg_len + L2CAP_CFG_OPTION_OVERHEAD) <= cmd_len) { p += cfg_len; if ((cfg_code & 0x80) == 0) { cfg_rej_len += cfg_len + L2CAP_CFG_OPTION_OVERHEAD; cfg_rej = TRUE; } } /* bad length; force loop exit */ else { p = p_cfg_end; cfg_rej = TRUE; } break; } } if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { p_ccb->remote_id = id; if (cfg_rej) { l2cu_send_peer_config_rej (p_ccb, p_cfg_start, (UINT16) (cmd_len - L2CAP_CONFIG_REQ_LEN), cfg_rej_len); } else { l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_REQ, &cfg_info); } } else { /* updated spec says send command reject on invalid cid */ l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_INVALID_CID, id, 0, 0); } break; case L2CAP_CMD_CONFIG_RSP: p_cfg_end = p + cmd_len; STREAM_TO_UINT16 (lcid, p); STREAM_TO_UINT16 (cfg_info.flags, p); STREAM_TO_UINT16 (cfg_info.result, p); cfg_info.flush_to_present = cfg_info.mtu_present = cfg_info.qos_present = cfg_info.fcr_present = cfg_info.fcs_present = FALSE; while (p < p_cfg_end) { STREAM_TO_UINT8 (cfg_code, p); STREAM_TO_UINT8 (cfg_len, p); switch (cfg_code & 0x7F) { case L2CAP_CFG_TYPE_MTU: cfg_info.mtu_present = TRUE; STREAM_TO_UINT16 (cfg_info.mtu, p); break; case L2CAP_CFG_TYPE_FLUSH_TOUT: cfg_info.flush_to_present = TRUE; STREAM_TO_UINT16 (cfg_info.flush_to, p); break; case L2CAP_CFG_TYPE_QOS: cfg_info.qos_present = TRUE; STREAM_TO_UINT8 (cfg_info.qos.qos_flags, p); STREAM_TO_UINT8 (cfg_info.qos.service_type, p); STREAM_TO_UINT32 (cfg_info.qos.token_rate, p); STREAM_TO_UINT32 (cfg_info.qos.token_bucket_size, p); STREAM_TO_UINT32 (cfg_info.qos.peak_bandwidth, p); STREAM_TO_UINT32 (cfg_info.qos.latency, p); STREAM_TO_UINT32 (cfg_info.qos.delay_variation, p); break; case L2CAP_CFG_TYPE_FCR: cfg_info.fcr_present = TRUE; STREAM_TO_UINT8 (cfg_info.fcr.mode, p); STREAM_TO_UINT8 (cfg_info.fcr.tx_win_sz, p); STREAM_TO_UINT8 (cfg_info.fcr.max_transmit, p); STREAM_TO_UINT16 (cfg_info.fcr.rtrans_tout, p); STREAM_TO_UINT16 (cfg_info.fcr.mon_tout, p); STREAM_TO_UINT16 (cfg_info.fcr.mps, p); break; case L2CAP_CFG_TYPE_FCS: cfg_info.fcs_present = TRUE; STREAM_TO_UINT8 (cfg_info.fcs, p); break; case L2CAP_CFG_TYPE_EXT_FLOW: cfg_info.ext_flow_spec_present = TRUE; STREAM_TO_UINT8 (cfg_info.ext_flow_spec.id, p); STREAM_TO_UINT8 (cfg_info.ext_flow_spec.stype, p); STREAM_TO_UINT16 (cfg_info.ext_flow_spec.max_sdu_size, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.sdu_inter_time, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.access_latency, p); STREAM_TO_UINT32 (cfg_info.ext_flow_spec.flush_timeout, p); break; } } if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { if (p_ccb->local_id != id) { L2CAP_TRACE_WARNING2 ("L2CAP - cfg rsp - bad ID. Exp: %d Got: %d", p_ccb->local_id, id); break; } if ( (cfg_info.result == L2CAP_CFG_OK) || (cfg_info.result == L2CAP_CFG_PENDING) ) l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP, &cfg_info); else l2c_csm_execute (p_ccb, L2CEVT_L2CAP_CONFIG_RSP_NEG, &cfg_info); } else { L2CAP_TRACE_WARNING1 ("L2CAP - rcvd cfg rsp for unknown CID: 0x%04x", lcid); } break; case L2CAP_CMD_DISC_REQ: STREAM_TO_UINT16 (lcid, p); STREAM_TO_UINT16 (rcid, p); if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { if (p_ccb->remote_cid == rcid) { p_ccb->remote_id = id; l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_REQ, &con_info); } } else l2cu_send_peer_disc_rsp (p_lcb, id, lcid, rcid); break; case L2CAP_CMD_DISC_RSP: STREAM_TO_UINT16 (rcid, p); STREAM_TO_UINT16 (lcid, p); if ((p_ccb = l2cu_find_ccb_by_cid (p_lcb, lcid)) != NULL) { if ((p_ccb->remote_cid == rcid) && (p_ccb->local_id == id)) { l2c_csm_execute (p_ccb, L2CEVT_L2CAP_DISCONNECT_RSP, &con_info); } } break; case L2CAP_CMD_ECHO_REQ: l2cu_send_peer_echo_rsp (p_lcb, id, p, cmd_len); break; case L2CAP_CMD_ECHO_RSP: if (p_lcb->p_echo_rsp_cb) { tL2CA_ECHO_RSP_CB *p_cb = p_lcb->p_echo_rsp_cb; /* Zero out the callback in case app immediately calls us again */ p_lcb->p_echo_rsp_cb = NULL; (*p_cb) (L2CAP_PING_RESULT_OK); } break; case L2CAP_CMD_INFO_REQ: STREAM_TO_UINT16 (info_type, p); l2cu_send_peer_info_rsp (p_lcb, id, info_type); break; case L2CAP_CMD_INFO_RSP: /* Stop the link connect timer if sent before L2CAP connection is up */ if (p_lcb->w4_info_rsp) { btu_stop_timer (&p_lcb->info_timer_entry); p_lcb->w4_info_rsp = FALSE; } STREAM_TO_UINT16 (info_type, p); STREAM_TO_UINT16 (result, p); p_lcb->info_rx_bits |= (1 << info_type); if ( (info_type == L2CAP_EXTENDED_FEATURES_INFO_TYPE) && (result == L2CAP_INFO_RESP_RESULT_SUCCESS) ) { STREAM_TO_UINT32( p_lcb->peer_ext_fea, p ); #if (L2CAP_NUM_FIXED_CHNLS > 0) if (p_lcb->peer_ext_fea & L2CAP_EXTFEA_FIXED_CHNLS) { l2cu_send_peer_info_req (p_lcb, L2CAP_FIXED_CHANNELS_INFO_TYPE); break; } else { l2cu_process_fixed_chnl_resp (p_lcb); } #endif } #if (L2CAP_NUM_FIXED_CHNLS > 0) if (info_type == L2CAP_FIXED_CHANNELS_INFO_TYPE) { if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) { memcpy (p_lcb->peer_chnl_mask, p, L2CAP_FIXED_CHNL_ARRAY_SIZE); } l2cu_process_fixed_chnl_resp (p_lcb); } #endif #if (L2CAP_UCD_INCLUDED == TRUE) else if (info_type == L2CAP_CONNLESS_MTU_INFO_TYPE) { if (result == L2CAP_INFO_RESP_RESULT_SUCCESS) { STREAM_TO_UINT16 (p_lcb->ucd_mtu, p); } } #endif ci.status = HCI_SUCCESS; memcpy (ci.bd_addr, p_lcb->remote_bd_addr, sizeof(BD_ADDR)); for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb; p_ccb = p_ccb->p_next_ccb) { l2c_csm_execute (p_ccb, L2CEVT_L2CAP_INFO_RSP, &ci); } break; default: L2CAP_TRACE_WARNING1 ("L2CAP - bad cmd code: %d", cmd_code); l2cu_send_peer_cmd_reject (p_lcb, L2CAP_CMD_REJ_NOT_UNDERSTOOD, id, 0, 0); return; } } }
/******************************************************************************* ** ** Function smp_data_received ** ** Description This function is called when data is received from L2CAP on ** SMP channel. ** ** ** Returns void ** *******************************************************************************/ static void smp_data_received(UINT16 channel, BD_ADDR bd_addr, BT_HDR *p_buf) { tSMP_CB *p_cb = &smp_cb; UINT8 *p = (UINT8 *)(p_buf + 1) + p_buf->offset; UINT8 cmd ; SMP_TRACE_EVENT ("SMDBG l2c %s", __FUNCTION__); STREAM_TO_UINT8(cmd, p); /* sanity check */ if ((SMP_OPCODE_MAX < cmd) || (SMP_OPCODE_MIN > cmd)) { SMP_TRACE_WARNING( "Ignore received command with RESERVED code 0x%02x", cmd); GKI_freebuf (p_buf); return; } /* reject the pairing request if there is an on-going SMP pairing */ if (SMP_OPCODE_PAIRING_REQ == cmd || SMP_OPCODE_SEC_REQ == cmd) { if ((p_cb->state == SMP_STATE_IDLE) && (p_cb->br_state == SMP_BR_STATE_IDLE) && !(p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD)) { p_cb->role = L2CA_GetBleConnRole(bd_addr); memcpy(&p_cb->pairing_bda[0], bd_addr, BD_ADDR_LEN); } else if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN)) { GKI_freebuf (p_buf); smp_reject_unexpected_pairing_command(bd_addr); return; } /* else, out of state pairing request/security request received, passed into SM */ } if (memcmp(&bd_addr[0], p_cb->pairing_bda, BD_ADDR_LEN) == 0) { btu_stop_timer (&p_cb->rsp_timer_ent); btu_start_timer (&p_cb->rsp_timer_ent, BTU_TTYPE_SMP_PAIRING_CMD, SMP_WAIT_FOR_RSP_TOUT); if (cmd == SMP_OPCODE_CONFIRM) { SMP_TRACE_DEBUG ("in %s cmd = 0x%02x, peer_auth_req = 0x%02x," "loc_auth_req = 0x%02x", __FUNCTION__, cmd, p_cb->peer_auth_req, p_cb->loc_auth_req); if ((p_cb->peer_auth_req & SMP_SC_SUPPORT_BIT) && (p_cb->loc_auth_req & SMP_SC_SUPPORT_BIT)) { cmd = SMP_OPCODE_PAIR_COMMITM; } } p_cb->rcvd_cmd_code = cmd; p_cb->rcvd_cmd_len = (UINT8) p_buf->len; smp_sm_event(p_cb, cmd, p); } GKI_freebuf (p_buf); }