/******************************************************************************* ** ** Function rfc_port_sm_state_closed ** ** Description This function handles events when the port is in ** CLOSED state. This state exists when port is ** being initially established. ** ** Returns void ** *******************************************************************************/ void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data) { switch (event) { case RFC_EVENT_OPEN: p_port->rfc.state = RFC_STATE_ORIG_WAIT_SEC_CHECK; btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, TRUE, BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), &rfc_sec_check_complete, p_port); return; case RFC_EVENT_CLOSE: break; case RFC_EVENT_CLEAR: return; case RFC_EVENT_DATA: GKI_freebuf (p_data); break; case RFC_EVENT_SABME: /* make sure the multiplexer disconnect timer is not running (reconnect case) */ rfc_timer_stop(p_port->rfc.p_mcb ); /* Open will be continued after security checks are passed */ p_port->rfc.state = RFC_STATE_TERM_WAIT_SEC_CHECK; btm_sec_mx_access_request (p_port->rfc.p_mcb->bd_addr, BT_PSM_RFCOMM, FALSE, BTM_SEC_PROTO_RFCOMM, (UINT32)(p_port->dlci / 2), &rfc_sec_check_complete, p_port); return; case RFC_EVENT_UA: return; case RFC_EVENT_DM: rfc_port_closed (p_port); return; case RFC_EVENT_UIH: GKI_freebuf (p_data); rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); return; case RFC_EVENT_DISC: rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); return; case RFC_EVENT_TIMEOUT: Port_TimeOutCloseMux( p_port->rfc.p_mcb ) ; RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); return; } RFCOMM_TRACE_WARNING ("Port state closed Event ignored %d", event); return; }
/******************************************************************************* ** ** Function rfc_mx_sm_state_idle ** ** Description This function handles events when the multiplexer is in ** IDLE state. This state exists when connection is being ** initially established. ** ** Returns void ** *******************************************************************************/ void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data) { RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_idle - evt:%d", event); switch (event) { case RFC_MX_EVENT_START_REQ: /* Initialize L2CAP MTU */ p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1; if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { PORT_StartCnf (p_mcb, RFCOMM_ERROR); return; } /* Save entry for quicker access to mcb based on the LCID */ rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; return; case RFC_MX_EVENT_START_RSP: case RFC_MX_EVENT_CONN_CNF: case RFC_MX_EVENT_CONF_IND: case RFC_MX_EVENT_CONF_CNF: RFCOMM_TRACE_ERROR2 ("Mx error state %d event %d", p_mcb->state, event); return; case RFC_MX_EVENT_CONN_IND: rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); L2CA_ConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0); rfc_mx_send_config_req (p_mcb); p_mcb->state = RFC_MX_STATE_CONFIGURE; return; case RFC_EVENT_SABME: break; case RFC_EVENT_UA: case RFC_EVENT_DM: return; case RFC_EVENT_DISC: rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); return; case RFC_EVENT_UIH: rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); return; } RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); }
/******************************************************************************* ** ** Function rfc_process_pn ** ** Description This function handles DLC parameter negotiation frame. ** Record MTU and pass indication to the upper layer. ** *******************************************************************************/ void rfc_process_pn (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame) { tPORT *p_port; UINT8 dlci = p_frame->dlci; if (is_command) { /* Ignore if Multiplexer is being shut down */ if (p_mcb->state != RFC_MX_STATE_DISC_WAIT_UA) { PORT_ParNegInd (p_mcb, dlci, p_frame->u.pn.mtu, p_frame->u.pn.conv_layer, p_frame->u.pn.k); } else { rfc_send_dm(p_mcb, dlci, FALSE); RFCOMM_TRACE_WARNING("***** MX PN while disconnecting *****"); } return; } /* If we are not awaiting response just ignore it */ p_port = port_find_mcb_dlci_port (p_mcb, dlci); if ((p_port == NULL) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) { return; } p_port->rfc.expected_rsp &= ~RFC_RSP_PN; rfc_port_timer_stop (p_port); PORT_ParNegCnf (p_mcb, dlci, p_frame->u.pn.mtu, p_frame->u.pn.conv_layer, p_frame->u.pn.k); }
/******************************************************************************* ** ** Function rfc_port_sm_disc_wait_ua ** ** Description This function handles events when DISC on the DLC was ** sent and SM is waiting for UA or DM. ** ** Returns void ** *******************************************************************************/ void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data) { switch (event) { case RFC_EVENT_OPEN: case RFC_EVENT_ESTABLISH_RSP: RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); return; case RFC_EVENT_CLEAR: rfc_port_closed (p_port); return; case RFC_EVENT_DATA: GKI_freebuf (p_data); return; case RFC_EVENT_UA: p_port->rfc.p_mcb->is_disc_initiator = TRUE; /* Case falls through */ case RFC_EVENT_DM: rfc_port_closed (p_port); return; case RFC_EVENT_SABME: rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); return; case RFC_EVENT_DISC: rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); return; case RFC_EVENT_UIH: GKI_freebuf (p_data); rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, FALSE); return; case RFC_EVENT_TIMEOUT: rfc_port_closed (p_port); return; } RFCOMM_TRACE_WARNING ("Port state disc_wait_ua Event ignored %d", event); }
/******************************************************************************* ** ** Function rfc_mx_sm_state_wait_sabme ** ** Description This function handles events when the multiplexer is ** waiting for SABME on the acceptor side after configuration ** ** Returns void ** *******************************************************************************/ void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data) { RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_wait_sabme - evt:%d", event); switch (event) { case RFC_MX_EVENT_DISC_IND: p_mcb->state = RFC_MX_STATE_IDLE; PORT_CloseInd (p_mcb); return; case RFC_EVENT_SABME: /* if we gave up outgoing connection request */ if (p_mcb->pending_lcid) { p_mcb->pending_lcid = 0; rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); rfc_timer_stop (p_mcb); p_mcb->state = RFC_MX_STATE_CONNECTED; p_mcb->peer_ready = TRUE; /* MX channel collision has been resolved, continue to open ports */ PORT_StartCnf (p_mcb, RFCOMM_SUCCESS); } else { rfc_timer_stop (p_mcb); PORT_StartInd (p_mcb); } return; case RFC_MX_EVENT_START_RSP: if (*((UINT16 *)p_data) != RFCOMM_SUCCESS) rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, TRUE); else { rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); p_mcb->state = RFC_MX_STATE_CONNECTED; p_mcb->peer_ready = TRUE; } return; case RFC_MX_EVENT_CONF_IND: /* workaround: we don't support reconfig */ case RFC_MX_EVENT_CONF_CNF: /* workaround: we don't support reconfig */ case RFC_EVENT_TIMEOUT: p_mcb->state = RFC_MX_STATE_IDLE; L2CA_DisconnectReq (p_mcb->lcid); PORT_CloseInd (p_mcb); return; } RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); }
/******************************************************************************* ** ** Function PORT_ParNegInd ** ** Description This function is called from the RFCOMM layer to change ** DLCI parameters (currently only MTU is negotiated). ** If can not find the port do not accept the request. ** Otherwise save the MTU size supported by the peer. ** *******************************************************************************/ void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k) { tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci); UINT8 our_cl; UINT8 our_k; RFCOMM_TRACE_EVENT ("PORT_ParNegInd dlci:%d mtu:%d", dlci, mtu); if (!p_port) { /* This can be a first request for this port */ p_port = port_find_dlci_port (dlci); if (!p_port) { /* If the port cannot be opened, send a DM. Per Errata 1205 */ rfc_send_dm(p_mcb, dlci, FALSE); /* check if this is the last port open, some headsets have problem, they don't disconnect if we send DM */ rfc_check_mcb_active( p_mcb ); RFCOMM_TRACE_EVENT( "PORT_ParNegInd: port not found" ); return; } p_mcb->port_inx[dlci] = p_port->inx; } memcpy (p_port->bd_addr, p_mcb->bd_addr, BD_ADDR_LEN); /* Connection is up and we know local and remote features, select MTU */ port_select_mtu (p_port); p_port->rfc.p_mcb = p_mcb; p_port->mtu = (p_port->mtu < mtu) ? p_port->mtu : mtu; p_port->peer_mtu = p_port->mtu; /* Negotiate the flow control mechanism. If flow control mechanism for */ /* mux has not been set yet, set it now. If either we or peer wants TS 07.10, */ /* use that. Otherwise both must want credit based, so use that. If flow is */ /* already defined for this mux, we respond with that value. */ if (p_mcb->flow == PORT_FC_UNDEFINED) { if ((PORT_FC_DEFAULT == PORT_FC_TS710) || (cl == RFCOMM_PN_CONV_LAYER_TYPE_1)) { p_mcb->flow = PORT_FC_TS710; } else { p_mcb->flow = PORT_FC_CREDIT; } } /* Regardless of our flow control mechanism, if the PN cl is zero, we must */ /* respond with zero. "A responding implementation must set this field to 14 */ /* if (and only if) the PN request was 15." This could happen if a PN is sent */ /* after the DLCI is already established-- the PN in that case must have cl = 0. */ /* See RFCOMM spec 5.5.3 */ if (cl == RFCOMM_PN_CONV_LAYER_TYPE_1) { our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; our_k = 0; } else if (p_mcb->flow == PORT_FC_CREDIT) { /* get credits */ port_get_credits (p_port, k); /* Set convergence layer and number of credits (k) */ our_cl = RFCOMM_PN_CONV_LAYER_CBFC_R; our_k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX; p_port->credit_rx = our_k; } else { /* must not be using credit based flow control; use TS 7.10 */ our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1; our_k = 0; } RFCOMM_ParNegRsp (p_mcb, dlci, p_port->mtu, our_cl, our_k); }
/******************************************************************************* ** ** Function rfc_port_sm_term_wait_sec_check ** ** Description This function handles events for the port in the ** WAIT_SEC_CHECK state. SABME has been received from the ** peer and Security Manager verifes BD_ADDR, before we can ** send ESTABLISH_IND to the Port entity ** ** Returns void ** *******************************************************************************/ void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data) { switch (event) { case RFC_EVENT_SEC_COMPLETE: if (*((UINT8 *)p_data) != BTM_SUCCESS) { /* Authentication/authorization failed. If link is still */ /* up send DM and check if we need to start inactive timer */ if (p_port->rfc.p_mcb) { rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); p_port->rfc.p_mcb->is_disc_initiator = TRUE; port_rfc_closed (p_port, PORT_SEC_FAILED); } } else { PORT_DlcEstablishInd (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu); } return; case RFC_EVENT_OPEN: case RFC_EVENT_CLOSE: RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event); return; case RFC_EVENT_CLEAR: btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); rfc_port_closed (p_port); return; case RFC_EVENT_DATA: RFCOMM_TRACE_ERROR ("Port error state Term Wait Sec event Data"); osi_free (p_data); return; case RFC_EVENT_SABME: /* Ignore SABME retransmission if client dares to do so */ return; case RFC_EVENT_DISC: btm_sec_abort_access_req (p_port->rfc.p_mcb->bd_addr); p_port->rfc.state = RFC_STATE_CLOSED; rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci); return; case RFC_EVENT_UIH: osi_free (p_data); return; case RFC_EVENT_ESTABLISH_RSP: if (*((UINT8 *)p_data) != RFCOMM_SUCCESS) { if (p_port->rfc.p_mcb) { rfc_send_dm (p_port->rfc.p_mcb, p_port->dlci, TRUE); } } else { rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci); p_port->rfc.state = RFC_STATE_OPENED; } return; } RFCOMM_TRACE_WARNING ("Port state term_wait_sec_check Event ignored %d", event); }
/******************************************************************************* ** ** Function RFCOMM_BufDataInd ** ** Description This is a callback function called by L2CAP when ** data RFCOMM frame is received. Parse the frames, check ** the checksum and dispatch event to multiplexer or port ** state machine depending on the frame destination. ** *******************************************************************************/ void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf) { tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid); tPORT *p_port; UINT8 event; if (!p_mcb) { RFCOMM_TRACE_WARNING ("RFCOMM_BufDataInd LCID:0x%x", lcid); GKI_freebuf (p_buf); return; } event = rfc_parse_data (p_mcb, &rfc_cb.rfc.rx_frame, p_buf); /* If the frame did not pass validation just ignore it */ if (event == RFC_EVENT_BAD_FRAME) { GKI_freebuf (p_buf); return; } if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) { /* Take special care of the Multiplexer Control Messages */ if (event == RFC_EVENT_UIH) { rfc_process_mx_message (p_mcb, p_buf); return; } /* Other multiplexer events go to state machine */ rfc_mx_sm_execute (p_mcb, event, NULL); GKI_freebuf (p_buf); return; } /* The frame was received on the data channel DLCI, verify that DLC exists */ if (((p_port = port_find_mcb_dlci_port (p_mcb, rfc_cb.rfc.rx_frame.dlci)) == NULL) || (!p_port->rfc.p_mcb)) { /* If this is a SABME on the new port, check if any appl is waiting for it */ if (event != RFC_EVENT_SABME) { if (( p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr) || (!p_mcb->is_initiator && rfc_cb.rfc.rx_frame.cr)) rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf); GKI_freebuf (p_buf); return; } if ((p_port = port_find_dlci_port (rfc_cb.rfc.rx_frame.dlci)) == NULL) { rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, TRUE); GKI_freebuf (p_buf); return; } p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx; p_port->rfc.p_mcb = p_mcb; } if (event == RFC_EVENT_UIH) { if (p_buf->len > 0) rfc_port_sm_execute (p_port, event, p_buf); else GKI_freebuf (p_buf); if (rfc_cb.rfc.rx_frame.credit != 0) rfc_inc_credit (p_port, rfc_cb.rfc.rx_frame.credit); return; } rfc_port_sm_execute (p_port, event, NULL); GKI_freebuf (p_buf); }
/******************************************************************************* ** ** Function rfc_mx_sm_state_disc_wait_ua ** ** Description This function handles events when the multiplexer sent ** DISC and is waiting for UA reply. ** ** Returns void ** *******************************************************************************/ void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data) { BT_HDR *p_buf; RFCOMM_TRACE_EVENT1 ("rfc_mx_sm_state_disc_wait_ua - evt:%d", event); switch (event) { case RFC_EVENT_UA: case RFC_EVENT_DM: case RFC_EVENT_TIMEOUT: L2CA_DisconnectReq (p_mcb->lcid); if (p_mcb->restart_required) { /* Start Request was received while disconnecting. Execute it again */ if ((p_mcb->lcid = L2CA_ConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr)) == 0) { PORT_StartCnf (p_mcb, RFCOMM_ERROR); return; } /* Save entry for quicker access to mcb based on the LCID */ rfc_save_lcid_mcb (p_mcb, p_mcb->lcid); /* clean up before reuse it */ while ((p_buf = (BT_HDR *)GKI_dequeue(&p_mcb->cmd_q)) != NULL) GKI_freebuf(p_buf); rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER); p_mcb->is_initiator = TRUE; p_mcb->restart_required = FALSE; p_mcb->local_cfg_sent = FALSE; p_mcb->peer_cfg_rcvd = FALSE; p_mcb->state = RFC_MX_STATE_WAIT_CONN_CNF; return; } rfc_release_multiplexer_channel (p_mcb); return; case RFC_EVENT_DISC: rfc_send_ua (p_mcb, RFCOMM_MX_DLCI); return; case RFC_EVENT_UIH: GKI_freebuf (p_data); rfc_send_dm (p_mcb, RFCOMM_MX_DLCI, FALSE); return; case RFC_MX_EVENT_START_REQ: p_mcb->restart_required = TRUE; return; case RFC_MX_EVENT_DISC_IND: p_mcb->state = RFC_MX_STATE_IDLE; PORT_CloseInd (p_mcb); return; case RFC_MX_EVENT_CLOSE_REQ: return; case RFC_MX_EVENT_QOS_VIOLATION_IND: break; } RFCOMM_TRACE_EVENT2 ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state); }