Fork ESP-IDF's bluetooth component

i want better sbc encoding, and no cla will stop me
This commit is contained in:
jacqueline
2024-03-28 14:32:49 +11:00
parent 239e6d8950
commit ee29c25b29
1761 changed files with 737738 additions and 0 deletions
@@ -0,0 +1,248 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/*****************************************************************************
*
* This file contains definitions internal to the PORT unit
*
*****************************************************************************/
#ifndef PORT_INT_H
#define PORT_INT_H
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "osi/fixed_queue.h"
#include "common/bt_defs.h"
/* Local events passed when application event is sent from the api to PORT */
/* ???*/
#define PORT_EVENT_OPEN (1 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_CONTROL (2 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_SET_STATE (3 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_SET_CALLBACK (5 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_WRITE (6 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_PURGE (7 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_SEND_ERROR (8 | BT_EVT_TO_BTU_SP_EVT)
#define PORT_EVENT_FLOW_CONTROL (9 | BT_EVT_TO_BTU_SP_EVT)
/*
** Flow control configuration values for the mux
*/
#define PORT_FC_UNDEFINED 0 /* mux flow control mechanism not defined yet */
#define PORT_FC_TS710 1 /* use TS 07.10 flow control */
#define PORT_FC_CREDIT 2 /* use RFCOMM credit based flow control */
/*
** Define Port Data Transfere control block
*/
typedef struct {
fixed_queue_t *queue; /* Queue of buffers waiting to be sent */
BOOLEAN peer_fc; /* TRUE if flow control is set based on peer's request */
BOOLEAN user_fc; /* TRUE if flow control is set based on user's request */
UINT32 queue_size; /* Number of data bytes in the queue */
tPORT_CALLBACK *p_callback; /* Address of the callback function */
} tPORT_DATA;
/*
** Port control structure used to pass modem info
*/
typedef struct {
#define MODEM_SIGNAL_DTRDSR 0x01
#define MODEM_SIGNAL_RTSCTS 0x02
#define MODEM_SIGNAL_RI 0x04
#define MODEM_SIGNAL_DCD 0x08
UINT8 modem_signal; /* [DTR/DSR | RTS/CTS | RI | DCD ] */
UINT8 break_signal; /* 0-3 s in steps of 200 ms */
UINT8 discard_buffers; /* 0 - do not discard, 1 - discard */
#define RFCOMM_CTRL_BREAK_ASAP 0
#define RFCOMM_CTRL_BREAK_IN_SEQ 1
UINT8 break_signal_seq; /* as soon as possible | in sequence (default) */
BOOLEAN fc; /* TRUE when the device is unable to accept frames */
} tPORT_CTRL;
/*
** RFCOMM multiplexer Control Block
*/
typedef struct {
TIMER_LIST_ENT tle; /* Timer list entry */
fixed_queue_t *cmd_q; /* Queue for command messages on this mux */
UINT8 port_inx[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */
/* tPORT based on dlci */
BD_ADDR bd_addr; /* BD ADDR of the peer if initiator */
UINT16 lcid; /* Local cid used for this channel */
UINT16 peer_l2cap_mtu; /* Max frame that can be sent to peer L2CAP */
UINT8 state; /* Current multiplexer channel state */
UINT8 is_initiator; /* TRUE if this side sends SABME (dlci=0) */
BOOLEAN local_cfg_sent;
BOOLEAN peer_cfg_rcvd;
BOOLEAN restart_required; /* TRUE if has to restart channel after disc */
BOOLEAN peer_ready; /* True if other side can accept frames */
UINT8 flow; /* flow control mechanism for this mux */
BOOLEAN l2cap_congested; /* TRUE if L2CAP is congested */
BOOLEAN is_disc_initiator; /* TRUE if initiated disc of port */
UINT16 pending_lcid; /* store LCID for incoming connection while connecting */
UINT8 pending_id; /* store l2cap ID for incoming connection while connecting */
} tRFC_MCB;
/*
** RFCOMM Port Connection Control Block
*/
struct t_rfc_port {
#define RFC_PORT_STATE_IDLE 0
#define RFC_PORT_STATE_WAIT_START 1
#define RFC_PORT_STATE_OPENING 2
#define RFC_PORT_STATE_OPENED 3
#define RFC_PORT_STATE_CLOSING 4
UINT8 state; /* Current state of the connection */
#define RFC_RSP_PN 0x01
#define RFC_RSP_RPN_REPLY 0x02
#define RFC_RSP_RPN 0x04
#define RFC_RSP_MSC 0x08
#define RFC_RSP_RLS 0x10
UINT8 expected_rsp;
tRFC_MCB *p_mcb;
TIMER_LIST_ENT tle; /* Timer list entry */
};
typedef struct t_rfc_port tRFC_PORT;
/*
** Define control block containing information about PORT connection
*/
struct t_port_info {
UINT8 inx; /* Index of this control block in the port_info array */
BOOLEAN in_use; /* True when structure is allocated */
#define PORT_STATE_CLOSED 0
#define PORT_STATE_OPENING 1
#define PORT_STATE_OPENED 2
#define PORT_STATE_CLOSING 3
UINT8 state; /* State of the application */
UINT8 scn; /* Service channel number */
UINT16 uuid; /* Service UUID */
BD_ADDR bd_addr; /* BD ADDR of the device for the multiplexer channel */
BOOLEAN is_server; /* TRUE if the server application */
UINT8 dlci; /* DLCI of the connection */
UINT8 error; /* Last error detected */
UINT8 line_status; /* Line status as reported by peer */
UINT8 default_signal_state; /* Initial signal state depending on uuid */
UINT16 mtu; /* Max MTU that port can receive */
UINT16 peer_mtu; /* Max MTU that port can send */
tPORT_DATA tx; /* Control block for data from app to peer */
tPORT_DATA rx; /* Control block for data from peer to app */
tPORT_STATE user_port_pars; /* Port parameters for user connection */
tPORT_STATE peer_port_pars; /* Port parameters for user connection */
tPORT_CTRL local_ctrl;
tPORT_CTRL peer_ctrl;
#define PORT_CTRL_REQ_SENT 0x01
#define PORT_CTRL_REQ_CONFIRMED 0x02
#define PORT_CTRL_IND_RECEIVED 0x04
#define PORT_CTRL_IND_RESPONDED 0x08
UINT8 port_ctrl; /* Modem Status Command */
BOOLEAN rx_flag_ev_pending; /* RXFLAG Character is received */
tRFC_PORT rfc; /* RFCOMM port control block */
UINT32 ev_mask; /* Event mask for the callback */
tPORT_CALLBACK *p_callback; /* Pointer to users callback function */
tPORT_MGMT_CALLBACK *p_mgmt_callback; /* Callback function to receive connection up/down */
tPORT_DATA_CALLBACK *p_data_callback; /* Callback function to receive data indications */
tPORT_DATA_CO_CALLBACK *p_data_co_callback; /* Callback function with callouts and flowctrl */
UINT16 credit_tx; /* Flow control credits for tx path */
UINT16 credit_rx; /* Flow control credits for rx path, this is */
/* number of buffers peer is allowed to sent */
UINT16 credit_rx_max; /* Max number of credits we will allow this guy to sent */
UINT16 credit_rx_low; /* Number of credits when we send credit update */
UINT16 rx_buf_critical; /* port receive queue critical watermark level */
BOOLEAN keep_port_handle; /* TRUE if port is not deallocated when closing */
/* it is set to TRUE for server when allocating port */
UINT16 keep_mtu; /* Max MTU that port can receive by server */
};
typedef struct t_port_info tPORT;
/* Define the PORT/RFCOMM control structure
*/
typedef struct {
tPORT port[MAX_RFC_PORTS]; /* Port info pool */
tRFC_MCB rfc_mcb[MAX_BD_CONNECTIONS]; /* RFCOMM bd_connections pool */
BOOLEAN enable_l2cap_ertm; /* enable/disable l2cap ertm */
} tPORT_CB;
#ifdef __cplusplus
extern "C" {
#endif
/*
** Functions provided by the port_utils.c
*/
extern tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr);
extern void port_set_defaults (tPORT *p_port);
extern void port_select_mtu (tPORT *p_port);
extern void port_release_port (tPORT *p_port);
extern tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci);
extern tRFC_MCB *port_find_mcb (BD_ADDR bd_addr);
extern tPORT *port_find_dlci_port (UINT8 dlci);
extern tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr);
extern UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal);
extern UINT32 port_flow_control_user (tPORT *p_port);
extern void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count);
/*
** Functions provided by the port_rfc.c
*/
extern int port_open_continue (tPORT *p_port);
extern void port_start_port_open (tPORT *p_port);
extern void port_start_par_neg (tPORT *p_port);
extern void port_start_control (tPORT *p_port);
extern void port_start_close (tPORT *p_port);
extern void port_rfc_closed (tPORT *p_port, UINT8 res);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,378 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/*****************************************************************************
*
* This file contains definitions internal to the RFC unit
*
*****************************************************************************/
#ifndef RFC_INT_H
#define RFC_INT_H
#include "stack/l2c_api.h"
#include "port_int.h"
/*
** Define RFCOMM result codes
*/
#define RFCOMM_SUCCESS 0
#define RFCOMM_ERROR 1
#define RFCOMM_LOW_RESOURCES 2
#define RFCOMM_TRY_LATER 3
#define RFCOMM_USER_ERR 111
#define RFCOMM_SECURITY_ERR 112
/*
** Define max and min RFCOMM MTU (N1)
*/
#define RFCOMM_MIN_MTU 23
#define RFCOMM_MAX_MTU 32767
extern void RFCOMM_StartReq (tRFC_MCB *p_mcb);
extern void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result);
extern void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu);
extern void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result);
extern void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf);
extern void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci);
extern void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu);
extern void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k);
extern void RFCOMM_TestReq (UINT8 *p_data, UINT16 len);
#define RFCOMM_FLOW_STATE_DISABLE 0
#define RFCOMM_FLOW_STATE_ENABLE 1
extern void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 state);
extern void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars);
extern void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask);
extern void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars);
extern void RFCOMM_ControlRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars);
extern void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status);
/*
** Define logical struct used for sending and decoding MX frames
*/
typedef struct {
UINT8 dlci;
UINT8 type;
UINT8 cr;
UINT8 ea;
UINT8 pf;
UINT8 credit;
union {
struct {
UINT8 dlci;
UINT8 frame_type;
UINT8 conv_layer;
UINT8 priority;
UINT8 t1;
UINT16 mtu;
UINT8 n2;
UINT8 k;
} pn;
struct {
UINT8 *p_data;
UINT16 data_len;
} test;
struct {
UINT8 dlci;
UINT8 signals;
UINT8 break_present;
UINT8 break_duration;
} msc;
struct {
UINT8 ea;
UINT8 cr;
UINT8 type;
} nsc;
struct {
UINT8 dlci;
UINT8 is_request;
UINT8 baud_rate;
UINT8 byte_size;
UINT8 stop_bits;
UINT8 parity;
UINT8 parity_type;
UINT8 fc_type;
UINT8 xon_char;
UINT8 xoff_char;
UINT16 param_mask;
} rpn;
struct {
UINT8 dlci;
UINT8 line_status;
} rls;
} u;
} MX_FRAME;
#define LINE_STATUS_NO_ERROR 0x00
#define LINE_STATUS_OVERRUN 0x02 /* Receive Overrun Error */
#define LINE_STATUS_RXPARITY 0x04 /* Receive Parity Error */
#define LINE_STATUS_FRAME 0x08 /* Receive Framing error */
#define LINE_STATUS_FAILED 0x10 /* Connection Failed */
/*
** Define states and events for the RFC multiplexer state machine
*/
#define RFC_MX_STATE_IDLE 0
#define RFC_MX_STATE_WAIT_CONN_CNF 1
#define RFC_MX_STATE_CONFIGURE 2
#define RFC_MX_STATE_SABME_WAIT_UA 3
#define RFC_MX_STATE_WAIT_SABME 4
#define RFC_MX_STATE_CONNECTED 5
#define RFC_MX_STATE_DISC_WAIT_UA 6
/*
** Define port states
*/
#define RFC_STATE_CLOSED 0
#define RFC_STATE_SABME_WAIT_UA 1
#define RFC_STATE_ORIG_WAIT_SEC_CHECK 2
#define RFC_STATE_TERM_WAIT_SEC_CHECK 3
#define RFC_STATE_OPENED 4
#define RFC_STATE_DISC_WAIT_UA 5
/*
** Events that can be received by multiplexer as well as port state machines
*/
#define RFC_EVENT_SABME 0
#define RFC_EVENT_UA 1
#define RFC_EVENT_DM 2
#define RFC_EVENT_DISC 3
#define RFC_EVENT_UIH 4
#define RFC_EVENT_TIMEOUT 5
#define RFC_EVENT_BAD_FRAME 50
/*
** Multiplexer events
*/
#define RFC_MX_EVENT_START_REQ 6
#define RFC_MX_EVENT_START_RSP 7
#define RFC_MX_EVENT_CLOSE_REQ 8
#define RFC_MX_EVENT_CONN_CNF 9
#define RFC_MX_EVENT_CONN_IND 10
#define RFC_MX_EVENT_CONF_CNF 11
#define RFC_MX_EVENT_CONF_IND 12
#define RFC_MX_EVENT_QOS_VIOLATION_IND 13
#define RFC_MX_EVENT_DISC_IND 14
#define RFC_MX_EVENT_TEST_CMD 15
#define RFC_MX_EVENT_TEST_RSP 16
#define RFC_MX_EVENT_FCON_CMD 17
#define RFC_MX_EVENT_FCOFF_CMD 18
#define RFC_MX_EVENT_NSC 19
#define RFC_MX_EVENT_NSC_RSP 20
/*
** Port events
*/
#define RFC_EVENT_OPEN 9
#define RFC_EVENT_ESTABLISH_RSP 11
#define RFC_EVENT_CLOSE 12
#define RFC_EVENT_CLEAR 13
#define RFC_EVENT_DATA 14
#define RFC_EVENT_SEC_COMPLETE 15
// btla-specific ++
#define RFC_T1_TIMEOUT 20 /* seconds to wait for reply with Poll bit */
#define RFC_PORT_T1_TIMEOUT 60 /* seconds to wait for reply with Poll bit other than MX */
#define RFC_T2_TIMEOUT 20 /* timeout to wait for Mx UIH */
// btla-specific --
#define RFC_DISC_TIMEOUT 3 /* If something goes wrong and we send DISC we should not wait for min */
#define RFC_CLOSE_TIMEOUT 10
#define RFCOMM_CONN_TIMEOUT 120 /* first connection to be established on Mx */
/* Define RFComm control block
*/
typedef struct {
MX_FRAME rx_frame;
tL2CAP_APPL_INFO reg_info; /* L2CAP Registration info */
tRFC_MCB *p_rfc_lcid_mcb[MAX_L2CAP_CHANNELS]; /* MCB based on the L2CAP's lcid */
BOOLEAN peer_rx_disabled; /* If TRUE peer sent FCOFF */
UINT8 last_mux; /* Last mux allocated */
UINT8 last_port; /* Last port allocated */
} tRFCOMM_CB;
/* Main Control Block for the RFCOMM Layer (PORT and RFC) */
typedef struct {
tRFCOMM_CB rfc;
tPORT_CB port;
UINT8 trace_level;
} tRFC_CB;
#if RFC_DYNAMIC_MEMORY == FALSE
extern tRFC_CB rfc_cb;
#else
extern tRFC_CB *rfc_cb_ptr;
#define rfc_cb (*rfc_cb_ptr)
#endif
/* Timer running on the multiplexor channel while no DLCI connection is opened */
#define RFC_MCB_INIT_INACT_TIMER 60 /* in seconds */
/* Timer running on the multiplexor channel after last DLCI is released */
#define RFC_MCB_RELEASE_INACT_TIMER 2 /* in seconds */
/*
** Define RFCOMM frame processing errors
*/
#define RFCOMM_ERR_BAD_SABME 1
#define RFCOMM_ERR_BAD_UA 2
#define RFCOMM_ERR_BAD_DM 3
#define RFCOMM_ERR_BAD_DISC 4
#define RFCOMM_ERR_BAD_UIH 5
#ifdef RFCOMM_PRECALC_FCS
#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_sabme_fcs[cr][dlci]
#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_ua_fcs[cr][dlci]
#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_dm_fcs[cr][dlci]
#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_disc_fcs[cr][dlci]
#define RFCOMM_UIH_FCS(p_data, dlci) rfc_uih_fcs[dlci]
#else
extern UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p);
#define RFCOMM_SABME_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data)
#define RFCOMM_UA_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data)
#define RFCOMM_DM_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data)
#define RFCOMM_DISC_FCS(p_data, cr, dlci) rfc_calc_fcs(3, p_data)
#define RFCOMM_UIH_FCS(p_data, dlci) rfc_calc_fcs(2, p_data)
#endif
#ifdef __cplusplus
extern "C" {
#endif
extern void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
/*
** Functions provided by the rfc_port_fsm.c
*/
extern void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data);
extern void rfc_process_pn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame);
extern void rfc_process_msc (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame);
extern void rfc_process_rpn (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BOOLEAN is_request, MX_FRAME *p_frame);
extern void rfc_process_rls (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, MX_FRAME *p_frame);
extern void rfc_process_nsc (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame);
extern void rfc_process_test_rsp (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf);
extern void rfc_process_fcon (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command);
extern void rfc_process_fcoff (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command);
extern void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested);
/*
** Functions provided by the rfc_utils.c
*/
tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator);
extern void rfc_release_multiplexer_channel (tRFC_MCB *p_rfc_mcb);
extern void rfc_timer_start (tRFC_MCB *p_rfc_mcb, UINT16 timeout);
extern void rfc_timer_stop (tRFC_MCB *p_rfc_mcb);
extern void rfc_timer_free (tRFC_MCB *p_rfc_mcb);
extern void rfc_port_timer_start (tPORT *p_port, UINT16 tout);
extern void rfc_port_timer_stop (tPORT *p_port);
extern void rfc_port_timer_free (tPORT *p_port);
BOOLEAN rfc_check_uih_fcs (UINT8 dlci, UINT8 received_fcs);
BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs);
tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid);
extern void rfc_save_lcid_mcb (tRFC_MCB *p_rfc_mcb, UINT16 lcid);
extern void rfc_check_mcb_active (tRFC_MCB *p_mcb);
extern void rfc_port_closed (tPORT *p_port);
extern void rfc_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res);
extern void rfc_inc_credit (tPORT *p_port, UINT8 credit);
extern void rfc_dec_credit (tPORT *p_port);
extern void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf);
/*
** Functions provided by the rfc_ts_frames.c
*/
extern void rfc_send_sabme (tRFC_MCB *p_rfc_mcb, UINT8 dlci);
extern void rfc_send_ua (tRFC_MCB *p_rfc_mcb, UINT8 dlci);
extern void rfc_send_dm (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BOOLEAN pf);
extern void rfc_send_disc (tRFC_MCB *p_rfc_mcb, UINT8 dlci);
extern void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu,
UINT8 cl, UINT8 k);
extern void rfc_send_test (tRFC_MCB *p_rfc_mcb, BOOLEAN is_command, BT_HDR *p_buf);
extern void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command,
tPORT_CTRL *p_pars);
extern void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status);
extern void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command,
tPORT_STATE *p_pars, UINT16 mask);
extern void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command);
extern void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command);
extern void rfc_send_buf_uih (tRFC_MCB *p_rfc_mcb, UINT8 dlci, BT_HDR *p_buf);
extern void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit);
extern void rfc_process_mx_message (tRFC_MCB *p_rfc_mcb, BT_HDR *p_buf);
extern UINT8 rfc_parse_data (tRFC_MCB *p_rfc_mcb, MX_FRAME *p_frame, BT_HDR *p_buf);
/*
** Functions provided by the rfc_disp.c
*/
/* Call back functions from RFCOMM */
extern void rfcomm_l2cap_if_init (void);
extern void PORT_StartInd (tRFC_MCB *p_mcb);
extern void PORT_StartCnf (tRFC_MCB *p_mcb, UINT16 result);
extern void PORT_CloseInd (tRFC_MCB *p_mcb);
extern void Port_TimeOutCloseMux (tRFC_MCB *p_mcb);
extern void PORT_DlcEstablishInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu);
extern void PORT_DlcEstablishCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result);
extern void PORT_DataInd (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf);
extern void PORT_DlcReleaseInd (tRFC_MCB *p_mcb, UINT8 dlci);
extern void PORT_ParNegInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k);
extern void PORT_ParNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k);
extern void PORT_TestCnf (tRFC_MCB *p_mcb, UINT8 *p_data, UINT16 len);
extern void PORT_FlowInd (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN fc);
extern void PORT_PortNegInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 param_mask);
extern void PORT_PortNegCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars, UINT16 result);
extern void PORT_ControlInd (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars);
extern void PORT_ControlCnf (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars);
extern void PORT_LineStatusInd (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 line_status);
#ifdef __cplusplus
}
#endif
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,576 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* Port Emulation entity utilities
*
******************************************************************************/
#include <string.h>
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "port_int.h"
#include "rfc_int.h"
#include "stack/l2cdefs.h"
#include "btm_int.h"
#include "stack/btu.h"
#include "osi/mutex.h"
#include "osi/allocator.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
static const tPORT_STATE default_port_pars = {
PORT_BAUD_RATE_9600,
PORT_8_BITS,
PORT_ONESTOPBIT,
PORT_PARITY_NO,
PORT_ODD_PARITY,
PORT_FC_OFF,
0, /* No rx_char */
PORT_XON_DC1,
PORT_XOFF_DC3,
};
/*******************************************************************************
**
** Function port_allocate_port
**
** Description Look through the Port Control Blocks for a free one. Note
** that one server can open several ports with the same SCN
** if it can support simulteneous requests from different
** clients.
**
** Returns Pointer to the PORT or NULL if not found
**
*******************************************************************************/
tPORT *port_allocate_port (UINT8 dlci, BD_ADDR bd_addr)
{
tPORT *p_port = &rfc_cb.port.port[0];
UINT8 xx, yy;
for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) {
if (yy >= MAX_RFC_PORTS) {
yy = 0;
}
p_port = &rfc_cb.port.port[yy];
if (!p_port->in_use) {
memset (p_port, 0, sizeof (tPORT));
p_port->in_use = TRUE;
p_port->inx = yy + 1;
p_port->dlci = dlci;
memcpy (p_port->bd_addr, bd_addr, BD_ADDR_LEN);
/* During the open set default state for the port connection */
port_set_defaults (p_port);
rfc_cb.rfc.last_port = yy;
RFCOMM_TRACE_DEBUG("rfc_cb.port.port[%d]:%p allocated, last_port:%d", yy, p_port, rfc_cb.rfc.last_port);
RFCOMM_TRACE_DEBUG("port_allocate_port:bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
return (p_port);
}
}
/* If here, no free PORT found */
return (NULL);
}
/*******************************************************************************
**
** Function port_set_defaults
**
** Description Set defualt port parameters
**
**
*******************************************************************************/
void port_set_defaults (tPORT *p_port)
{
p_port->ev_mask = 0;
p_port->p_callback = NULL;
p_port->port_ctrl = 0;
p_port->error = 0;
p_port->line_status = 0;
p_port->rx_flag_ev_pending = FALSE;
p_port->peer_mtu = RFCOMM_DEFAULT_MTU;
p_port->user_port_pars = default_port_pars;
p_port->peer_port_pars = default_port_pars;
p_port->credit_tx = 0;
p_port->credit_rx = 0;
/* p_port->credit_rx_max = PORT_CREDIT_RX_MAX; Determined later */
/* p_port->credit_rx_low = PORT_CREDIT_RX_LOW; Determined later */
memset (&p_port->local_ctrl, 0, sizeof (p_port->local_ctrl));
memset (&p_port->peer_ctrl, 0, sizeof (p_port->peer_ctrl));
memset (&p_port->rx, 0, sizeof (p_port->rx));
memset (&p_port->tx, 0, sizeof (p_port->tx));
p_port->tx.queue = fixed_queue_new(QUEUE_SIZE_MAX);
p_port->rx.queue = fixed_queue_new(QUEUE_SIZE_MAX);
}
/*******************************************************************************
**
** Function port_select_mtu
**
** Description Select MTU which will best serve connection from our
** point of view.
** If our device is 1.2 or lower we calculate how many DH5s
** fit into 1 RFCOMM buffer.
**
**
*******************************************************************************/
void port_select_mtu (tPORT *p_port)
{
UINT16 packet_size;
/* Will select MTU only if application did not setup something */
if (p_port->mtu == 0) {
/* find packet size which connection supports */
packet_size = btm_get_max_packet_size (p_port->bd_addr);
if (packet_size == 0) {
/* something is very wrong */
RFCOMM_TRACE_WARNING ("port_select_mtu bad packet size");
p_port->mtu = RFCOMM_DEFAULT_MTU;
} else {
/* We try to negotiate MTU that each packet can be split into whole
number of max packets. For example if link is 1.2 max packet size is 339 bytes.
At first calculate how many whole packets it is. MAX L2CAP is 1691 + 4 overhead.
1695, that will be 5 Dh5 packets. Now maximum RFCOMM packet is
5 * 339 = 1695. Minus 4 bytes L2CAP header 1691. Minus RFCOMM 6 bytes header overhead 1685
For EDR 2.0 packet size is 1027. So we better send RFCOMM packet as 1 3DH5 packet
1 * 1027 = 1027. Minus 4 bytes L2CAP header 1023. Minus RFCOMM 6 bytes header overhead 1017 */
if ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) >= packet_size) {
p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size * packet_size) - RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD;
RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on connection speed", p_port->mtu);
} else {
p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD;
RFCOMM_TRACE_DEBUG ("port_select_mtu selected %d based on l2cap PDU size", p_port->mtu);
}
}
} else {
RFCOMM_TRACE_DEBUG ("port_select_mtu application selected %d", p_port->mtu);
}
p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu);
if ( p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM ) {
p_port->credit_rx_max = PORT_RX_BUF_HIGH_WM;
}
p_port->credit_rx_low = (PORT_RX_LOW_WM / p_port->mtu);
if ( p_port->credit_rx_low > PORT_RX_BUF_LOW_WM ) {
p_port->credit_rx_low = PORT_RX_BUF_LOW_WM;
}
p_port->rx_buf_critical = (PORT_RX_CRITICAL_WM / p_port->mtu);
if ( p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM ) {
p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM;
}
RFCOMM_TRACE_DEBUG ("port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d",
p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical);
}
/*******************************************************************************
**
** Function port_release_port
**
** Description Release port infor control block.
**
** Returns Pointer to the PORT or NULL if not found
**
*******************************************************************************/
void port_release_port (tPORT *p_port)
{
BT_HDR *p_buf;
UINT32 mask;
tPORT_CALLBACK *p_port_cb;
tPORT_STATE user_port_pars;
osi_mutex_global_lock();
RFCOMM_TRACE_DEBUG("port_release_port, p_port:%p", p_port);
if (p_port->rx.queue != NULL) {
while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->rx.queue, 0)) != NULL) {
osi_free(p_buf);
}
}
p_port->rx.queue_size = 0;
if (p_port->tx.queue != NULL) {
while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_port->tx.queue, 0)) != NULL) {
osi_free(p_buf);
}
}
p_port->tx.queue_size = 0;
osi_mutex_global_unlock();
p_port->state = PORT_STATE_CLOSED;
if (p_port->rfc.state == RFC_STATE_CLOSED) {
RFCOMM_TRACE_DEBUG ("rfc_port_closed DONE");
if (p_port->rfc.p_mcb) {
p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0;
/* If there are no more ports opened on this MCB release it */
rfc_check_mcb_active (p_port->rfc.p_mcb);
}
rfc_port_timer_stop (p_port);
fixed_queue_free(p_port->tx.queue, NULL);
p_port->tx.queue = NULL;
fixed_queue_free(p_port->rx.queue, NULL);
p_port->rx.queue = NULL;
RFCOMM_TRACE_DEBUG ("port_release_port:p_port->keep_port_handle:%d", p_port->keep_port_handle);
if ( p_port->keep_port_handle ) {
RFCOMM_TRACE_DEBUG ("port_release_port:Initialize handle:%d", p_port->inx);
/* save event mask and callback */
mask = p_port->ev_mask;
p_port_cb = p_port->p_callback;
user_port_pars = p_port->user_port_pars;
port_set_defaults(p_port);
/* restore */
p_port->ev_mask = mask;
p_port->p_callback = p_port_cb;
p_port->user_port_pars = user_port_pars;
p_port->mtu = p_port->keep_mtu;
p_port->state = PORT_STATE_OPENING;
p_port->rfc.p_mcb = NULL;
if (p_port->is_server) {
p_port->dlci &= 0xfe;
}
p_port->local_ctrl.modem_signal = p_port->default_signal_state;
memcpy (p_port->bd_addr, BT_BD_ANY, BD_ADDR_LEN);
} else {
RFCOMM_TRACE_DEBUG ("port_release_port:Clean-up handle:%d", p_port->inx);
rfc_port_timer_free (p_port);
memset (p_port, 0, sizeof (tPORT));
}
}
}
/*******************************************************************************
**
** Function port_find_mcb
**
** Description This function checks if connection exists to device with
** the BD_ADDR.
**
*******************************************************************************/
tRFC_MCB *port_find_mcb (BD_ADDR bd_addr)
{
int i;
for (i = 0; i < MAX_BD_CONNECTIONS; i++) {
if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE)
&& !memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN)) {
/* Multiplexer channel found do not change anything */
RFCOMM_TRACE_DEBUG("port_find_mcb: found bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
RFCOMM_TRACE_DEBUG("port_find_mcb: rfc_cb.port.rfc_mcb:index:%d, %p, lcid:%d",
i, &rfc_cb.port.rfc_mcb[i], rfc_cb.port.rfc_mcb[i].lcid);
return (&rfc_cb.port.rfc_mcb[i]);
}
}
RFCOMM_TRACE_DEBUG("port_find_mcb: not found, bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
return (NULL);
}
/*******************************************************************************
**
** Function port_find_mcb_dlci_port
**
** Description Find port on the multiplexer channel based on DLCI. If
** this port with DLCI not found try to use even DLCI. This
** is for the case when client is establishing connection on
** none-initiator MCB.
**
** Returns Pointer to the PORT or NULL if not found
**
*******************************************************************************/
tPORT *port_find_mcb_dlci_port (tRFC_MCB *p_mcb, UINT8 dlci)
{
UINT8 inx;
if (!p_mcb) {
return (NULL);
}
if (dlci > RFCOMM_MAX_DLCI) {
return (NULL);
}
inx = p_mcb->port_inx[dlci];
if (inx == 0) {
RFCOMM_TRACE_DEBUG("port_find_mcb_dlci_port: p_mcb:%p, port_inx[dlci:%d] is 0", p_mcb, dlci);
return (NULL);
} else {
return (&rfc_cb.port.port[inx - 1]);
}
}
/*******************************************************************************
**
** Function port_find_dlci_port
**
** Description Find port with DLCI not assigned to multiplexer channel
**
** Returns Pointer to the PORT or NULL if not found
**
*******************************************************************************/
tPORT *port_find_dlci_port (UINT8 dlci)
{
UINT16 i;
tPORT *p_port;
for (i = 0; i < MAX_RFC_PORTS; i++) {
p_port = &rfc_cb.port.port[i];
if (p_port->in_use && (p_port->rfc.p_mcb == NULL)) {
if (p_port->dlci == dlci) {
return (p_port);
} else if ((dlci & 0x01) && (p_port->dlci == (dlci - 1))) {
p_port->dlci++;
return (p_port);
}
}
}
return (NULL);
}
/*******************************************************************************
**
** Function port_find_port
**
** Description Find port with DLCI, BD_ADDR
**
** Returns Pointer to the PORT or NULL if not found
**
*******************************************************************************/
tPORT *port_find_port (UINT8 dlci, BD_ADDR bd_addr)
{
UINT16 i;
tPORT *p_port;
for (i = 0; i < MAX_RFC_PORTS; i++) {
p_port = &rfc_cb.port.port[i];
if (p_port->in_use
&& (p_port->dlci == dlci)
&& !memcmp (p_port->bd_addr, bd_addr, BD_ADDR_LEN)) {
return (p_port);
}
}
return (NULL);
}
/*******************************************************************************
**
** Function port_flow_control_user
**
** Description Check the current user flow control and if necessary return
** events to be send to the user based on the user's specified
** flow control type.
**
** Returns event mask to be returned to the application
**
*******************************************************************************/
UINT32 port_flow_control_user (tPORT *p_port)
{
UINT32 event = 0;
/* Flow control to the user can be caused by flow controlling by the peer */
/* (FlowInd, or flow control by the peer RFCOMM (Fcon) or internally if */
/* tx_queue is full */
BOOLEAN fc = p_port->tx.peer_fc
|| !p_port->rfc.p_mcb
|| !p_port->rfc.p_mcb->peer_ready
|| (p_port->tx.queue_size > PORT_TX_HIGH_WM)
|| (fixed_queue_length(p_port->tx.queue) > PORT_TX_BUF_HIGH_WM);
if (p_port->tx.user_fc == fc) {
return (0);
}
p_port->tx.user_fc = fc;
if (fc) {
event = PORT_EV_FC;
} else {
event = PORT_EV_FC | PORT_EV_FCS;
}
return (event);
}
/*******************************************************************************
**
** Function port_get_signal_changes
**
** Description Check modem signals that has been changed
**
** Returns event mask to be returned to the application
**
*******************************************************************************/
UINT32 port_get_signal_changes (tPORT *p_port, UINT8 old_signals, UINT8 signal)
{
UINT8 changed_signals = (signal ^ old_signals);
UINT32 events = 0;
if (changed_signals & PORT_DTRDSR_ON) {
events |= PORT_EV_DSR;
if (signal & PORT_DTRDSR_ON) {
events |= PORT_EV_DSRS;
}
}
if (changed_signals & PORT_CTSRTS_ON) {
events |= PORT_EV_CTS;
if (signal & PORT_CTSRTS_ON) {
events |= PORT_EV_CTSS;
}
}
if (changed_signals & PORT_RING_ON) {
events |= PORT_EV_RING;
}
if (changed_signals & PORT_DCD_ON) {
events |= PORT_EV_RLSD;
if (signal & PORT_DCD_ON) {
events |= PORT_EV_RLSDS;
}
}
return (p_port->ev_mask & events);
}
/*******************************************************************************
**
** Function port_flow_control_peer
**
** Description Send flow control messages to the peer for both enabling
** and disabling flow control, for both credit-based and
** TS 07.10 flow control mechanisms.
**
** Returns nothing
**
*******************************************************************************/
void port_flow_control_peer(tPORT *p_port, BOOLEAN enable, UINT16 count)
{
if (!p_port->rfc.p_mcb) {
return;
}
/* If using credit based flow control */
if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
/* if want to enable flow from peer */
if (enable) {
/* update rx credits */
if (count > p_port->credit_rx) {
p_port->credit_rx = 0;
} else {
p_port->credit_rx -= count;
}
/* If credit count is less than low credit watermark, and user */
/* did not force flow control, send a credit update */
/* There might be a special case when we just adjusted rx_max */
if ((p_port->credit_rx <= p_port->credit_rx_low)
&& !p_port->rx.user_fc
&& (p_port->credit_rx_max > p_port->credit_rx)) {
rfc_send_credit(p_port->rfc.p_mcb, p_port->dlci,
(UINT8) (p_port->credit_rx_max - p_port->credit_rx));
RFCOMM_TRACE_DEBUG("send credit: max %d, rx %d, count %d", p_port->credit_rx_max, p_port->credit_rx, count);
p_port->credit_rx = p_port->credit_rx_max;
p_port->rx.peer_fc = FALSE;
} else {
RFCOMM_TRACE_DEBUG("credit: max %d, rx %d, low %d", p_port->credit_rx_max, p_port->credit_rx, p_port->credit_rx_low);
}
}
/* else want to disable flow from peer */
else {
/* if client registered data callback, just do what they want */
if (p_port->p_data_callback || p_port->p_data_co_callback) {
p_port->rx.peer_fc = TRUE;
}
/* if queue count reached credit rx max, set peer fc */
else if (fixed_queue_length(p_port->rx.queue) >= p_port->credit_rx_max) {
p_port->rx.peer_fc = TRUE;
}
}
}
/* else using TS 07.10 flow control */
else {
/* if want to enable flow from peer */
if (enable) {
/* If rfcomm suspended traffic from the peer based on the rx_queue_size */
/* check if it can be resumed now */
if (p_port->rx.peer_fc
&& (p_port->rx.queue_size < PORT_RX_LOW_WM)
&& (fixed_queue_length(p_port->rx.queue) < PORT_RX_BUF_LOW_WM)) {
p_port->rx.peer_fc = FALSE;
/* If user did not force flow control allow traffic now */
if (!p_port->rx.user_fc) {
RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, TRUE);
}
}
}
/* else want to disable flow from peer */
else {
/* if client registered data callback, just do what they want */
if (p_port->p_data_callback || p_port->p_data_co_callback) {
p_port->rx.peer_fc = TRUE;
RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE);
}
/* Check the size of the rx queue. If it exceeds certain */
/* level and flow control has not been sent to the peer do it now */
else if ( ((p_port->rx.queue_size > PORT_RX_HIGH_WM)
|| (fixed_queue_length(p_port->rx.queue) > PORT_RX_BUF_HIGH_WM))
&& !p_port->rx.peer_fc) {
RFCOMM_TRACE_EVENT ("PORT_DataInd Data reached HW. Sending FC set.");
p_port->rx.peer_fc = TRUE;
RFCOMM_FlowReq (p_port->rfc.p_mcb, p_port->dlci, FALSE);
}
}
}
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,432 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains L2CAP interface functions
*
******************************************************************************/
#include <stddef.h>
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "port_int.h"
#include "stack/l2c_api.h"
#include "stack/l2cdefs.h"
#include "rfc_int.h"
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#include "osi/alarm.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
static tL2CAP_ERTM_INFO rfc_l2c_etm_opt =
{
L2CAP_FCR_ERTM_MODE,
L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */
L2CAP_USER_RX_BUF_SIZE,
L2CAP_USER_TX_BUF_SIZE,
L2CAP_FCR_RX_BUF_SIZE,
L2CAP_FCR_TX_BUF_SIZE
};
/*
** Define Callback functions to be called by L2CAP
*/
static void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
static void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 err);
static void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
static void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
static void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_clear);
static void RFCOMM_QoSViolationInd (BD_ADDR bd_addr);
static void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf);
static void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested);
/*******************************************************************************
**
** Function rfcomm_l2cap_if_init
**
** Description This function is called during the RFCOMM task startup
** to register interface functions with L2CAP.
**
*******************************************************************************/
void rfcomm_l2cap_if_init (void)
{
tL2CAP_APPL_INFO *p_l2c = &rfc_cb.rfc.reg_info;
p_l2c->pL2CA_ConnectInd_Cb = RFCOMM_ConnectInd;
p_l2c->pL2CA_ConnectCfm_Cb = RFCOMM_ConnectCnf;
p_l2c->pL2CA_ConnectPnd_Cb = NULL;
p_l2c->pL2CA_ConfigInd_Cb = RFCOMM_ConfigInd;
p_l2c->pL2CA_ConfigCfm_Cb = RFCOMM_ConfigCnf;
p_l2c->pL2CA_DisconnectInd_Cb = RFCOMM_DisconnectInd;
p_l2c->pL2CA_DisconnectCfm_Cb = NULL;
p_l2c->pL2CA_QoSViolationInd_Cb = RFCOMM_QoSViolationInd;
p_l2c->pL2CA_DataInd_Cb = RFCOMM_BufDataInd;
p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd;
p_l2c->pL2CA_TxComplete_Cb = NULL;
L2CA_Register (BT_PSM_RFCOMM, p_l2c);
}
/*******************************************************************************
**
** Function RFCOMM_ConnectInd
**
** Description This is a callback function called by L2CAP when
** L2CA_ConnectInd received. Allocate multiplexer control block
** and dispatch the event to it.
**
*******************************************************************************/
void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
{
tRFC_MCB *p_mcb = rfc_alloc_multiplexer_channel(bd_addr, FALSE);
UNUSED(psm);
if ((p_mcb) && (p_mcb->state != RFC_MX_STATE_IDLE)) {
/* if this is collision case */
if ((p_mcb->is_initiator) && (p_mcb->state == RFC_MX_STATE_WAIT_CONN_CNF)) {
p_mcb->pending_lcid = lcid;
p_mcb->pending_id = id;
/* wait random timeout (2 - 12) to resolve collision */
/* if peer gives up then local device rejects incoming connection and continues as initiator */
/* if timeout, local device disconnects outgoing connection and continues as acceptor */
RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectInd start timer for collision, initiator's LCID(0x%x), acceptor's LCID(0x%x)",
p_mcb->lcid, p_mcb->pending_lcid);
rfc_timer_start(p_mcb, (UINT16)(osi_time_get_os_boottime_ms() % 10 + 2));
return;
} else {
/* we cannot accept connection request from peer at this state */
/* don't update lcid */
p_mcb = NULL;
}
} else {
/* store mcb even if null */
rfc_save_lcid_mcb (p_mcb, lcid);
}
if (p_mcb == NULL) {
tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL;
L2CA_ErtmConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0, ertm_opt);
return;
}
p_mcb->lcid = lcid;
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &id);
}
/*******************************************************************************
**
** Function RFCOMM_ConnectCnf
**
** Description This is a callback function called by L2CAP when
** L2CA_ConnectCnf received. Save L2CAP handle and dispatch
** event to the FSM.
**
*******************************************************************************/
void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result)
{
tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
if (!p_mcb) {
RFCOMM_TRACE_ERROR ("RFCOMM_ConnectCnf LCID:0x%x", lcid);
return;
}
if (p_mcb->pending_lcid) {
/* if peer rejects our connect request but peer's connect request is pending */
if (result != L2CAP_CONN_OK ) {
UINT16 i;
UINT8 idx;
RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf retry as acceptor on pending LCID(0x%x)", p_mcb->pending_lcid);
/* remove mcb from mapping table */
rfc_save_lcid_mcb (NULL, p_mcb->lcid);
p_mcb->lcid = p_mcb->pending_lcid;
p_mcb->is_initiator = FALSE;
p_mcb->state = RFC_MX_STATE_IDLE;
/* store mcb into mapping table */
rfc_save_lcid_mcb (p_mcb, p_mcb->lcid);
/* update direction bit */
for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) {
if ((idx = p_mcb->port_inx[i]) != 0) {
p_mcb->port_inx[i] = 0;
p_mcb->port_inx[i + 1] = idx;
rfc_cb.port.port[idx - 1].dlci += 1;
RFCOMM_TRACE_DEBUG ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci);
}
}
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id));
return;
} else {
RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid);
tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL;
/* Peer gave up his connection request, make sure cleaning up L2CAP channel */
L2CA_ErtmConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0, ertm_opt);
p_mcb->pending_lcid = 0;
}
}
/* Save LCID to be used in all consecutive calls to L2CAP */
p_mcb->lcid = lcid;
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_CNF, &result);
}
/*******************************************************************************
**
** Function RFCOMM_ConfigInd
**
** Description This is a callback function called by L2CAP when
** L2CA_ConfigInd received. Save parameters in the control
** block and dispatch event to the FSM.
**
*******************************************************************************/
void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
{
tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
if (!p_mcb) {
RFCOMM_TRACE_ERROR ("RFCOMM_ConfigInd LCID:0x%x", lcid);
return;
}
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_IND, (void *)p_cfg);
}
/*******************************************************************************
**
** Function RFCOMM_ConfigCnf
**
** Description This is a callback function called by L2CAP when
** L2CA_ConfigCnf received. Save L2CAP handle and dispatch
** event to the FSM.
**
*******************************************************************************/
void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
{
tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
if (!p_mcb) {
RFCOMM_TRACE_ERROR ("RFCOMM_ConfigCnf no MCB LCID:0x%x", lcid);
return;
}
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_CNF, (void *)p_cfg);
}
/*******************************************************************************
**
** Function RFCOMM_QoSViolationInd
**
** Description This is a callback function called by L2CAP when
** L2CA_QoSViolationIndInd received. Dispatch event to the FSM.
**
*******************************************************************************/
void RFCOMM_QoSViolationInd (BD_ADDR bd_addr)
{
UNUSED(bd_addr);
}
/*******************************************************************************
**
** Function RFCOMM_DisconnectInd
**
** Description This is a callback function called by L2CAP when
** L2CA_DisconnectInd received. Dispatch event to the FSM.
**
*******************************************************************************/
void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_conf_needed)
{
tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
if (is_conf_needed) {
L2CA_DisconnectRsp (lcid);
}
if (!p_mcb) {
RFCOMM_TRACE_WARNING ("RFCOMM_DisconnectInd LCID:0x%x", lcid);
return;
}
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_DISC_IND, NULL);
}
/*******************************************************************************
**
** 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);
osi_free (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) {
osi_free (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);
osi_free (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);
}
osi_free (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);
osi_free (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 {
osi_free (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);
osi_free (p_buf);
}
/*******************************************************************************
**
** Function RFCOMM_CongestionStatusInd
**
** Description This is a callback function called by L2CAP when
** data RFCOMM L2CAP congestion status changes
**
*******************************************************************************/
void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested)
{
tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
if (!p_mcb) {
RFCOMM_TRACE_ERROR ("RFCOMM_CongestionStatusInd dropped LCID:0x%x", lcid);
return;
} else {
RFCOMM_TRACE_EVENT ("RFCOMM_CongestionStatusInd LCID:0x%x", lcid);
}
rfc_process_l2cap_congestion (p_mcb, is_congested);
}
/*******************************************************************************
**
** Function rfc_find_lcid_mcb
**
** Description This function returns MCB block supporting local cid
**
*******************************************************************************/
tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid)
{
tRFC_MCB *p_mcb;
if (lcid - L2CAP_BASE_APPL_CID >= MAX_L2CAP_CHANNELS) {
RFCOMM_TRACE_ERROR ("rfc_find_lcid_mcb LCID:0x%x", lcid);
return (NULL);
} else {
if ((p_mcb = rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID]) != NULL) {
if (p_mcb->lcid != lcid) {
RFCOMM_TRACE_WARNING ("rfc_find_lcid_mcb LCID reused LCID:0x%x current:0x%x", lcid, p_mcb->lcid);
return (NULL);
}
}
}
return (p_mcb);
}
/*******************************************************************************
**
** Function rfc_save_lcid_mcb
**
** Description This function returns MCB block supporting local cid
**
*******************************************************************************/
void rfc_save_lcid_mcb (tRFC_MCB *p_mcb, UINT16 lcid)
{
rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb;
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,683 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains state machine and action routines for multiplexer
* channel of the RFCOMM unit
*
******************************************************************************/
#include <string.h>
#include "stack/bt_types.h"
#include "stack/rfcdefs.h"
#include "stack/l2cdefs.h"
#include "stack/port_api.h"
#include "port_int.h"
#include "stack/l2c_api.h"
#include "rfc_int.h"
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#include "common/bt_target.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
#define L2CAP_SUCCESS 0
#define L2CAP_ERROR 1
static tL2CAP_ERTM_INFO rfc_l2c_etm_opt =
{
L2CAP_FCR_ERTM_MODE,
L2CAP_FCR_CHAN_OPT_ERTM|L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */
L2CAP_USER_RX_BUF_SIZE,
L2CAP_USER_TX_BUF_SIZE,
L2CAP_FCR_RX_BUF_SIZE,
L2CAP_FCR_TX_BUF_SIZE
};
static tL2CAP_FCR_OPTS rfc_l2c_fcr_opts_def =
{
L2CAP_FCR_ERTM_MODE,
RFC_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
RFC_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */
RFC_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
RFC_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
RFC_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */
};
/********************************************************************************/
/* L O C A L F U N C T I O N P R O T O T Y P E S */
/********************************************************************************/
static void rfc_mx_sm_state_idle (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_state_wait_sabme (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_sm_state_disc_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data);
static void rfc_mx_send_config_req (tRFC_MCB *p_mcb);
static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg);
static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg);
/*******************************************************************************
**
** Function rfc_mx_sm_execute
**
** Description This function sends multiplexor events through the state
** machine.
**
** Returns void
**
*******************************************************************************/
void rfc_mx_sm_execute (tRFC_MCB *p_mcb, UINT16 event, void *p_data)
{
RFCOMM_TRACE_DEBUG("%s st:%d, evt:%d\n", __func__, p_mcb->state, event);
switch (p_mcb->state) {
case RFC_MX_STATE_IDLE:
rfc_mx_sm_state_idle (p_mcb, event, p_data);
break;
case RFC_MX_STATE_WAIT_CONN_CNF:
rfc_mx_sm_state_wait_conn_cnf (p_mcb, event, p_data);
break;
case RFC_MX_STATE_CONFIGURE:
rfc_mx_sm_state_configure (p_mcb, event, p_data);
break;
case RFC_MX_STATE_SABME_WAIT_UA:
rfc_mx_sm_sabme_wait_ua (p_mcb, event, p_data);
break;
case RFC_MX_STATE_WAIT_SABME:
rfc_mx_sm_state_wait_sabme (p_mcb, event, p_data);
break;
case RFC_MX_STATE_CONNECTED:
rfc_mx_sm_state_connected (p_mcb, event, p_data);
break;
case RFC_MX_STATE_DISC_WAIT_UA:
rfc_mx_sm_state_disc_wait_ua (p_mcb, event, p_data);
break;
}
}
/*******************************************************************************
**
** 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)
{
tL2CAP_ERTM_INFO *ertm_opt = NULL;
RFCOMM_TRACE_EVENT ("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;
ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL;
p_mcb->lcid = L2CA_ErtmConnectReq (BT_PSM_RFCOMM, p_mcb->bd_addr, ertm_opt);
if (p_mcb->lcid == 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_ERROR ("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);
ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL;
L2CA_ErtmConnectRsp (p_mcb->bd_addr, *((UINT8 *)p_data), p_mcb->lcid, L2CAP_CONN_OK, 0, ertm_opt);
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_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** Function rfc_mx_sm_state_wait_conn_cnf
**
** Description This function handles events when the multiplexer is
** waiting for Connection Confirm from L2CAP.
**
** Returns void
**
*******************************************************************************/
void rfc_mx_sm_state_wait_conn_cnf (tRFC_MCB *p_mcb, UINT16 event, void *p_data)
{
RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_wait_conn_cnf - evt:%d", event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event);
return;
/* There is some new timing so that Config Ind comes before security is completed
so we are still waiting fo the confirmation. */
case RFC_MX_EVENT_CONF_IND:
rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data);
return;
case RFC_MX_EVENT_CONN_CNF:
if (*((UINT16 *)p_data) != L2CAP_SUCCESS) {
p_mcb->state = RFC_MX_STATE_IDLE;
PORT_StartCnf (p_mcb, *((UINT16 *)p_data));
return;
}
p_mcb->state = RFC_MX_STATE_CONFIGURE;
rfc_mx_send_config_req (p_mcb);
return;
case RFC_MX_EVENT_DISC_IND:
p_mcb->state = RFC_MX_STATE_IDLE;
PORT_CloseInd (p_mcb);
return;
case RFC_EVENT_TIMEOUT:
p_mcb->state = RFC_MX_STATE_IDLE;
L2CA_DisconnectReq (p_mcb->lcid);
/* we gave up outgoing connection request then try peer's request */
if (p_mcb->pending_lcid) {
UINT16 i;
UINT8 idx;
RFCOMM_TRACE_DEBUG ("RFCOMM MX retry as acceptor in collision case - evt:%d in state:%d", event, p_mcb->state);
rfc_save_lcid_mcb (NULL, p_mcb->lcid);
p_mcb->lcid = p_mcb->pending_lcid;
rfc_save_lcid_mcb (p_mcb, p_mcb->lcid);
p_mcb->is_initiator = FALSE;
/* update direction bit */
for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) {
if ((idx = p_mcb->port_inx[i]) != 0) {
p_mcb->port_inx[i] = 0;
p_mcb->port_inx[i + 1] = idx;
rfc_cb.port.port[idx - 1].dlci += 1;
RFCOMM_TRACE_DEBUG ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci);
}
}
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id));
} else {
PORT_CloseInd (p_mcb);
}
return;
}
RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** Function rfc_mx_sm_state_configure
**
** Description This function handles events when the multiplexer in the
** configuration state.
**
** Returns void
**
*******************************************************************************/
void rfc_mx_sm_state_configure (tRFC_MCB *p_mcb, UINT16 event, void *p_data)
{
RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_configure - evt:%d", event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
case RFC_MX_EVENT_CONN_CNF:
RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event);
return;
case RFC_MX_EVENT_CONF_IND:
rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data);
return;
case RFC_MX_EVENT_CONF_CNF:
rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data);
return;
case RFC_MX_EVENT_DISC_IND:
p_mcb->state = RFC_MX_STATE_IDLE;
PORT_CloseInd (p_mcb);
return;
case RFC_EVENT_TIMEOUT:
p_mcb->state = RFC_MX_STATE_IDLE;
L2CA_DisconnectReq (p_mcb->lcid);
PORT_StartCnf (p_mcb, RFCOMM_ERROR);
return;
}
RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** Function rfc_mx_sm_sabme_wait_ua
**
** Description This function handles events when the multiplexer sent
** SABME and is waiting for UA reply.
**
** Returns void
**
*******************************************************************************/
void rfc_mx_sm_sabme_wait_ua (tRFC_MCB *p_mcb, UINT16 event, void *p_data)
{
UNUSED(p_data);
RFCOMM_TRACE_EVENT ("rfc_mx_sm_sabme_wait_ua - evt:%d", event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
case RFC_MX_EVENT_CONN_CNF:
RFCOMM_TRACE_ERROR ("Mx error state %d event %d", p_mcb->state, event);
return;
/* workaround: we don't support reconfig */
/* commented out until we support reconfig
case RFC_MX_EVENT_CONF_IND:
rfc_mx_conf_ind (p_mcb, (tL2CAP_CFG_INFO *)p_data);
return;
case RFC_MX_EVENT_CONF_CNF:
rfc_mx_conf_cnf (p_mcb, (tL2CAP_CFG_INFO *)p_data);
return;
*/
case RFC_MX_EVENT_DISC_IND:
p_mcb->state = RFC_MX_STATE_IDLE;
PORT_CloseInd (p_mcb);
return;
case RFC_EVENT_UA:
rfc_timer_stop (p_mcb);
p_mcb->state = RFC_MX_STATE_CONNECTED;
p_mcb->peer_ready = TRUE;
PORT_StartCnf (p_mcb, RFCOMM_SUCCESS);
return;
case RFC_EVENT_DM:
rfc_timer_stop (p_mcb);
/* Case falls through */
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_StartCnf (p_mcb, RFCOMM_ERROR);
return;
}
RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** 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_EVENT ("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_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** Function rfc_mx_sm_state_connected
**
** Description This function handles events when the multiplexer is
** in the CONNECTED state
**
** Returns void
**
*******************************************************************************/
void rfc_mx_sm_state_connected (tRFC_MCB *p_mcb, UINT16 event, void *p_data)
{
UNUSED(p_data);
RFCOMM_TRACE_EVENT ("rfc_mx_sm_state_connected - evt:%d", event);
switch (event) {
case RFC_EVENT_TIMEOUT:
case RFC_MX_EVENT_CLOSE_REQ:
rfc_timer_start (p_mcb, RFC_DISC_TIMEOUT);
p_mcb->state = RFC_MX_STATE_DISC_WAIT_UA;
rfc_send_disc (p_mcb, RFCOMM_MX_DLCI);
return;
case RFC_MX_EVENT_DISC_IND:
p_mcb->state = RFC_MX_STATE_IDLE;
PORT_CloseInd (p_mcb);
return;
case RFC_EVENT_DISC:
/* Reply with UA. If initiator bring down L2CAP connection */
/* If server wait for some time if client decide to reinitiate channel */
rfc_send_ua (p_mcb, RFCOMM_MX_DLCI);
if (p_mcb->is_initiator) {
L2CA_DisconnectReq (p_mcb->lcid);
}
/* notify all ports that connection is gone */
PORT_CloseInd (p_mcb);
return;
}
RFCOMM_TRACE_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** 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_EVENT ("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) {
tL2CAP_ERTM_INFO *ertm_opt = rfc_cb.port.enable_l2cap_ertm ? &rfc_l2c_etm_opt : NULL;
/* Start Request was received while disconnecting. Execute it again */
p_mcb->lcid = L2CA_ErtmConnectReq(BT_PSM_RFCOMM, p_mcb->bd_addr, ertm_opt);
if (p_mcb->lcid == 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 *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) != NULL) {
osi_free(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:
osi_free (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_EVENT ("RFCOMM MX ignored - evt:%d in state:%d", event, p_mcb->state);
}
/*******************************************************************************
**
** Function rfc_mx_send_config_req
**
** Description This function handles L2CA_ConnectInd message from the
** L2CAP. Accept connection.
**
*******************************************************************************/
static void rfc_mx_send_config_req (tRFC_MCB *p_mcb)
{
tL2CAP_CFG_INFO cfg;
RFCOMM_TRACE_EVENT ("rfc_mx_send_config_req");
memset (&cfg, 0, sizeof (tL2CAP_CFG_INFO));
cfg.mtu_present = TRUE;
cfg.mtu = L2CAP_MTU_SIZE;
if (rfc_cb.port.enable_l2cap_ertm) {
cfg.fcr_present = TRUE;
cfg.fcr = rfc_l2c_fcr_opts_def;
}
/* Defaults set by memset
cfg.flush_to_present = FALSE;
cfg.qos_present = FALSE;
cfg.fcr_present = FALSE;
cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
cfg.fcs_present = FALSE;
cfg.fcs = N/A when fcs_present is FALSE;
*/
L2CA_ConfigReq (p_mcb->lcid, &cfg);
}
/*******************************************************************************
**
** Function rfc_mx_conf_cnf
**
** Description This function handles L2CA_ConfigCnf message from the
** L2CAP. If result is not success tell upper layer that
** start has not been accepted. If initiator send SABME
** on DLCI 0. T1 is still running.
**
*******************************************************************************/
static void rfc_mx_conf_cnf (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg)
{
// RFCOMM_TRACE_EVENT ("rfc_mx_conf_cnf p_cfg:%08x res:%d ", p_cfg, (p_cfg) ? p_cfg->result : 0);
if (p_cfg->result != L2CAP_CFG_OK) {
if (p_mcb->is_initiator) {
PORT_StartCnf (p_mcb, p_cfg->result);
L2CA_DisconnectReq (p_mcb->lcid);
}
rfc_release_multiplexer_channel (p_mcb);
return;
}
p_mcb->local_cfg_sent = TRUE;
if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->peer_cfg_rcvd) {
if (p_mcb->is_initiator) {
p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA;
rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI);
rfc_timer_start (p_mcb, RFC_T1_TIMEOUT);
} else {
p_mcb->state = RFC_MX_STATE_WAIT_SABME;
rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); /* - increased from T2=20 to CONN=120
to allow the user more than 10 sec to type in the
pin which can be e.g. 16 digits */
}
}
}
/*******************************************************************************
**
** Function rfc_mx_conf_ind
**
** Description This function handles L2CA_ConfigInd message from the
** L2CAP. Send the L2CA_ConfigRsp message.
**
*******************************************************************************/
static void rfc_mx_conf_ind (tRFC_MCB *p_mcb, tL2CAP_CFG_INFO *p_cfg)
{
/* Save peer L2CAP MTU if present */
/* RFCOMM adds 3-4 bytes in the beginning and 1 bytes FCS */
if (p_cfg->mtu_present) {
p_mcb->peer_l2cap_mtu = p_cfg->mtu - RFCOMM_MIN_OFFSET - 1;
} else {
p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1;
}
p_cfg->mtu_present = FALSE;
p_cfg->flush_to_present = FALSE;
p_cfg->qos_present = FALSE;
p_cfg->result = L2CAP_CFG_OK;
L2CA_ConfigRsp (p_mcb->lcid, p_cfg);
p_mcb->peer_cfg_rcvd = TRUE;
if ((p_mcb->state == RFC_MX_STATE_CONFIGURE) && p_mcb->local_cfg_sent) {
if (p_mcb->is_initiator) {
p_mcb->state = RFC_MX_STATE_SABME_WAIT_UA;
rfc_send_sabme (p_mcb, RFCOMM_MX_DLCI);
rfc_timer_start (p_mcb, RFC_T1_TIMEOUT);
} else {
p_mcb->state = RFC_MX_STATE_WAIT_SABME;
rfc_timer_start (p_mcb, RFCOMM_CONN_TIMEOUT); /* - increased from T2=20 to CONN=120
to allow the user more than 10 sec to type in the
pin which can be e.g. 16 digits */
}
}
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,908 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains state machine and action routines for a port of the
* RFCOMM unit
*
******************************************************************************/
#include <string.h>
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/btm_api.h"
#include "btm_int.h"
#include "stack/port_api.h"
#include "port_int.h"
#include "rfc_int.h"
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
/********************************************************************************/
/* L O C A L F U N C T I O N P R O T O T Y P E S */
/********************************************************************************/
static void rfc_port_sm_state_closed (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_sm_sabme_wait_ua (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_sm_term_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_sm_disc_wait_ua (tPORT *p_port, UINT16 event, void *p_data);
static void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf);
static void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame);
/*******************************************************************************
**
** Function rfc_port_sm_execute
**
** Description This function sends port events through the state
** machine.
**
** Returns void
**
*******************************************************************************/
void rfc_port_sm_execute (tPORT *p_port, UINT16 event, void *p_data)
{
RFCOMM_TRACE_DEBUG("%s st:%d, evt:%d\n", __func__, p_port->rfc.state, event);
if (!p_port) {
RFCOMM_TRACE_WARNING ("NULL port event %d", event);
return;
}
switch (p_port->rfc.state) {
case RFC_STATE_CLOSED:
rfc_port_sm_state_closed (p_port, event, p_data);
break;
case RFC_STATE_SABME_WAIT_UA:
rfc_port_sm_sabme_wait_ua (p_port, event, p_data);
break;
case RFC_STATE_ORIG_WAIT_SEC_CHECK:
rfc_port_sm_orig_wait_sec_check (p_port, event, p_data);
break;
case RFC_STATE_TERM_WAIT_SEC_CHECK:
rfc_port_sm_term_wait_sec_check (p_port, event, p_data);
break;
case RFC_STATE_OPENED:
rfc_port_sm_opened (p_port, event, p_data);
break;
case RFC_STATE_DISC_WAIT_UA:
rfc_port_sm_disc_wait_ua (p_port, event, p_data);
break;
}
}
/*******************************************************************************
**
** 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:
osi_free (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:
osi_free (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_port_sm_sabme_wait_ua
**
** Description This function handles events when SABME on the DLC was
** sent and SM is waiting for UA or DM.
**
** Returns void
**
*******************************************************************************/
void rfc_port_sm_sabme_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_CLOSE:
rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT);
rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci);
p_port->rfc.expected_rsp = 0;
p_port->rfc.state = RFC_STATE_DISC_WAIT_UA;
return;
case RFC_EVENT_CLEAR:
rfc_port_closed (p_port);
return;
case RFC_EVENT_DATA:
osi_free (p_data);
break;
case RFC_EVENT_UA:
rfc_port_timer_stop (p_port);
p_port->rfc.state = RFC_STATE_OPENED;
PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_SUCCESS);
return;
case RFC_EVENT_DM:
p_port->rfc.p_mcb->is_disc_initiator = TRUE;
PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR);
rfc_port_closed (p_port);
return;
case RFC_EVENT_DISC:
rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci);
PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR);
rfc_port_closed (p_port);
return;
case RFC_EVENT_SABME:
/* Continue to wait for the UA the SABME this side sent */
rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci);
return;
case RFC_EVENT_UIH:
osi_free (p_data);
return;
case RFC_EVENT_TIMEOUT:
p_port->rfc.state = RFC_STATE_CLOSED;
PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR);
return;
}
RFCOMM_TRACE_WARNING ("Port state sabme_wait_ua Event ignored %d", event);
}
/*******************************************************************************
**
** 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);
if (*((UINT8 *)p_data) == RFCOMM_LOW_RESOURCES) {
port_rfc_closed(p_port, PORT_NO_RESOURCES);
} else {
port_rfc_closed(p_port, PORT_UNKNOWN_ERROR);
}
}
} 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 rfc_port_sm_orig_wait_sec_check
**
** Description This function handles events for the port in the
** ORIG_WAIT_SEC_CHECK state. RFCOMM is waiting for Security
** manager to finish before sending SABME to the peer
**
** Returns void
**
*******************************************************************************/
void rfc_port_sm_orig_wait_sec_check (tPORT *p_port, UINT16 event, void *p_data)
{
switch (event) {
case RFC_EVENT_SEC_COMPLETE:
if (*((UINT8 *)p_data) != BTM_SUCCESS) {
p_port->rfc.p_mcb->is_disc_initiator = TRUE;
PORT_DlcEstablishCnf (p_port->rfc.p_mcb, p_port->dlci, 0, RFCOMM_SECURITY_ERR);
rfc_port_closed (p_port);
return;
}
rfc_send_sabme (p_port->rfc.p_mcb, p_port->dlci);
rfc_port_timer_start (p_port, RFC_PORT_T1_TIMEOUT);
p_port->rfc.state = RFC_STATE_SABME_WAIT_UA;
return;
case RFC_EVENT_OPEN:
case RFC_EVENT_SABME: /* Peer should not use the same dlci */
RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event);
return;
case RFC_EVENT_CLOSE:
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 Orig Wait Sec event Data");
osi_free (p_data);
return;
case RFC_EVENT_UIH:
osi_free (p_data);
return;
}
RFCOMM_TRACE_WARNING ("Port state orig_wait_sec_check Event ignored %d", event);
}
/*******************************************************************************
**
** Function rfc_port_sm_opened
**
** Description This function handles events for the port in the OPENED
** state
**
** Returns void
**
*******************************************************************************/
void rfc_port_sm_opened (tPORT *p_port, UINT16 event, void *p_data)
{
switch (event) {
case RFC_EVENT_OPEN:
RFCOMM_TRACE_ERROR ("Port error state %d event %d", p_port->rfc.state, event);
return;
case RFC_EVENT_CLOSE:
rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT);
rfc_send_disc (p_port->rfc.p_mcb, p_port->dlci);
p_port->rfc.expected_rsp = 0;
p_port->rfc.state = RFC_STATE_DISC_WAIT_UA;
return;
case RFC_EVENT_CLEAR:
rfc_port_closed (p_port);
return;
case RFC_EVENT_DATA:
/* Send credits in the frame. Pass them in the layer specific member of the hdr. */
/* There might be an initial case when we reduced rx_max and credit_rx is still */
/* bigger. Make sure that we do not send 255 */
if ((p_port->rfc.p_mcb->flow == PORT_FC_CREDIT)
&& (((BT_HDR *)p_data)->len < p_port->peer_mtu)
&& (!p_port->rx.user_fc)
&& (p_port->credit_rx_max > p_port->credit_rx)) {
((BT_HDR *)p_data)->layer_specific = (UINT8) (p_port->credit_rx_max - p_port->credit_rx);
p_port->credit_rx = p_port->credit_rx_max;
} else {
((BT_HDR *)p_data)->layer_specific = 0;
}
rfc_send_buf_uih (p_port->rfc.p_mcb, p_port->dlci, (BT_HDR *)p_data);
rfc_dec_credit (p_port);
return;
case RFC_EVENT_UA:
return;
case RFC_EVENT_SABME:
rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci);
return;
case RFC_EVENT_DM:
PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci);
rfc_port_closed (p_port);
return;
case RFC_EVENT_DISC:
p_port->rfc.state = RFC_STATE_CLOSED;
rfc_send_ua (p_port->rfc.p_mcb, p_port->dlci);
if (! fixed_queue_is_empty(p_port->rx.queue)) {
/* give a chance to upper stack to close port properly */
RFCOMM_TRACE_DEBUG("port queue is not empty");
rfc_port_timer_start (p_port, RFC_DISC_TIMEOUT);
} else {
PORT_DlcReleaseInd (p_port->rfc.p_mcb, p_port->dlci);
}
return;
case RFC_EVENT_UIH:
rfc_port_uplink_data (p_port, (BT_HDR *)p_data);
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 opened Event ignored %d", event);
}
/*******************************************************************************
**
** 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:
osi_free (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:
osi_free (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_port_uplink_data
**
** Description This function handles uplink information data frame.
**
*******************************************************************************/
void rfc_port_uplink_data (tPORT *p_port, BT_HDR *p_buf)
{
PORT_DataInd (p_port->rfc.p_mcb, p_port->dlci, p_buf);
}
/*******************************************************************************
**
** 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_process_rpn
**
** Description This function handles Remote DLC parameter negotiation
** command/response. Pass command to the user.
**
*******************************************************************************/
void rfc_process_rpn (tRFC_MCB *p_mcb, BOOLEAN is_command,
BOOLEAN is_request, MX_FRAME *p_frame)
{
tPORT_STATE port_pars;
tPORT *p_port;
if ((p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci)) == NULL) {
/* This is the first command on the port */
if (is_command) {
memset(&port_pars, 0, sizeof(tPORT_STATE));
rfc_set_port_state(&port_pars, p_frame);
PORT_PortNegInd(p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask);
}
return;
}
if (is_command && is_request) {
/* This is the special situation when peer just request local pars */
port_pars = p_port->peer_port_pars;
rfc_send_rpn (p_mcb, p_frame->dlci, FALSE, &p_port->peer_port_pars, 0);
return;
}
port_pars = p_port->peer_port_pars;
rfc_set_port_state(&port_pars, p_frame);
if (is_command) {
PORT_PortNegInd (p_mcb, p_frame->dlci, &port_pars, p_frame->u.rpn.param_mask);
return;
}
/* If we are not awaiting response just ignore it */
p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci);
if ((p_port == NULL) || !(p_port->rfc.expected_rsp & (RFC_RSP_RPN | RFC_RSP_RPN_REPLY))) {
return;
}
/* If we sent a request for port parameters to the peer he is replying with */
/* mask 0. */
rfc_port_timer_stop (p_port);
if (p_port->rfc.expected_rsp & RFC_RSP_RPN_REPLY) {
p_port->rfc.expected_rsp &= ~RFC_RSP_RPN_REPLY;
p_port->peer_port_pars = port_pars;
if ((port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT))
|| (port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT))) {
/* This is satisfactory port parameters. Set mask as it was Ok */
p_frame->u.rpn.param_mask = RFCOMM_RPN_PM_MASK;
} else {
/* Current peer parameters are not good, try to fix them */
p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT);
p_port->rfc.expected_rsp |= RFC_RSP_RPN;
rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars,
RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
return;
}
} else {
p_port->rfc.expected_rsp &= ~RFC_RSP_RPN;
}
/* Check if all suggested parameters were accepted */
if (((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT)) ==
(RFCOMM_RPN_PM_RTR_ON_INPUT | RFCOMM_RPN_PM_RTR_ON_OUTPUT))
|| ((p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT)) ==
(RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT))) {
PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS);
return;
}
/* If we were proposing RTR flow control try RTC flow control */
/* If we were proposing RTC flow control try no flow control */
/* otherwise drop the connection */
if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTR_ON_INPUT | RFCOMM_FC_RTR_ON_OUTPUT)) {
/* Current peer parameters are not good, try to fix them */
p_port->peer_port_pars.fc_type = (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT);
p_port->rfc.expected_rsp |= RFC_RSP_RPN;
rfc_send_rpn (p_mcb, p_frame->dlci, TRUE, &p_port->peer_port_pars,
RFCOMM_RPN_PM_RTC_ON_INPUT | RFCOMM_RPN_PM_RTC_ON_OUTPUT);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
return;
}
/* Other side does not support flow control */
if (p_port->peer_port_pars.fc_type == (RFCOMM_FC_RTC_ON_INPUT | RFCOMM_FC_RTC_ON_OUTPUT)) {
p_port->peer_port_pars.fc_type = RFCOMM_FC_OFF;
PORT_PortNegCnf (p_mcb, p_port->dlci, &port_pars, RFCOMM_SUCCESS);
}
}
/*******************************************************************************
**
** Function rfc_process_msc
**
** Description This function handles Modem Status Command.
** Pass command to the user.
**
*******************************************************************************/
void rfc_process_msc (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame)
{
tPORT_CTRL pars;
tPORT *p_port;
UINT8 modem_signals = p_frame->u.msc.signals;
BOOLEAN new_peer_fc = FALSE;
p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci);
if (p_port == NULL) {
return;
}
pars.modem_signal = 0;
if (modem_signals & RFCOMM_MSC_RTC) {
pars.modem_signal |= MODEM_SIGNAL_DTRDSR;
}
if (modem_signals & RFCOMM_MSC_RTR) {
pars.modem_signal |= MODEM_SIGNAL_RTSCTS;
}
if (modem_signals & RFCOMM_MSC_IC) {
pars.modem_signal |= MODEM_SIGNAL_RI;
}
if (modem_signals & RFCOMM_MSC_DV) {
pars.modem_signal |= MODEM_SIGNAL_DCD;
}
pars.fc = ((modem_signals & RFCOMM_MSC_FC) == RFCOMM_MSC_FC);
pars.break_signal = (p_frame->u.msc.break_present) ?
p_frame->u.msc.break_duration : 0;
pars.discard_buffers = 0;
pars.break_signal_seq = RFCOMM_CTRL_BREAK_IN_SEQ; /* this is default */
/* Check if this command is passed only to indicate flow control */
if (is_command) {
rfc_send_msc (p_mcb, p_frame->dlci, FALSE, &pars);
if (p_port->rfc.p_mcb->flow != PORT_FC_CREDIT) {
/* Spec 1.1 indicates that only FC bit is used for flow control */
p_port->peer_ctrl.fc = new_peer_fc = pars.fc;
if (new_peer_fc != p_port->tx.peer_fc) {
PORT_FlowInd (p_mcb, p_frame->dlci, (BOOLEAN)!new_peer_fc);
}
}
PORT_ControlInd (p_mcb, p_frame->dlci, &pars);
return;
}
/* If we are not awaiting response just ignore it */
if (!(p_port->rfc.expected_rsp & RFC_RSP_MSC)) {
return;
}
p_port->rfc.expected_rsp &= ~RFC_RSP_MSC;
rfc_port_timer_stop (p_port);
PORT_ControlCnf (p_port->rfc.p_mcb, p_port->dlci, &pars);
}
/*******************************************************************************
**
** Function rfc_process_rls
**
** Description This function handles Remote Line Status command.
** Pass command to the user.
**
*******************************************************************************/
void rfc_process_rls (tRFC_MCB *p_mcb, BOOLEAN is_command, MX_FRAME *p_frame)
{
tPORT *p_port;
if (is_command) {
PORT_LineStatusInd (p_mcb, p_frame->dlci, p_frame->u.rls.line_status);
rfc_send_rls (p_mcb, p_frame->dlci, FALSE, p_frame->u.rls.line_status);
} else {
p_port = port_find_mcb_dlci_port (p_mcb, p_frame->dlci);
/* If we are not awaiting response just ignore it */
if (!p_port || !(p_port->rfc.expected_rsp & RFC_RSP_RLS)) {
return;
}
p_port->rfc.expected_rsp &= ~RFC_RSP_RLS;
rfc_port_timer_stop (p_port);
}
}
/*******************************************************************************
**
** Function rfc_process_nsc
**
** Description This function handles None Supported Command frame.
**
*******************************************************************************/
void rfc_process_nsc (tRFC_MCB *p_mcb, MX_FRAME *p_frame)
{
UNUSED(p_mcb);
UNUSED(p_frame);
}
/*******************************************************************************
**
** Function rfc_process_test
**
** Description This function handles Test frame. If this is a command
** reply to it. Otherwise pass response to the user.
**
*******************************************************************************/
void rfc_process_test_rsp (tRFC_MCB *p_mcb, BT_HDR *p_buf)
{
UNUSED(p_mcb);
osi_free (p_buf);
}
/*******************************************************************************
**
** Function rfc_process_fcon
**
** Description This function handles FCON frame. The peer entity is able
** to receive new information
**
*******************************************************************************/
void rfc_process_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command)
{
if (is_command) {
rfc_cb.rfc.peer_rx_disabled = FALSE;
rfc_send_fcon (p_mcb, FALSE);
if (!p_mcb->l2cap_congested) {
PORT_FlowInd (p_mcb, 0, TRUE);
}
}
}
/*******************************************************************************
**
** Function rfc_process_fcoff
**
** Description This function handles FCOFF frame. The peer entity is unable
** to receive new information
**
*******************************************************************************/
void rfc_process_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command)
{
if (is_command) {
rfc_cb.rfc.peer_rx_disabled = TRUE;
if (!p_mcb->l2cap_congested) {
PORT_FlowInd (p_mcb, 0, FALSE);
}
rfc_send_fcoff (p_mcb, FALSE);
}
}
/*******************************************************************************
**
** Function rfc_process_l2cap_congestion
**
** Description This function handles L2CAP congestion messages
**
*******************************************************************************/
void rfc_process_l2cap_congestion (tRFC_MCB *p_mcb, BOOLEAN is_congested)
{
p_mcb->l2cap_congested = is_congested;
if (!is_congested) {
rfc_check_send_cmd(p_mcb, NULL);
}
if (!rfc_cb.rfc.peer_rx_disabled) {
if (!is_congested) {
PORT_FlowInd (p_mcb, 0, TRUE);
} else {
PORT_FlowInd (p_mcb, 0, FALSE);
}
}
}
/*******************************************************************************
**
** Function rfc_set_port_pars
**
** Description This function sets the tPORT_STATE structure given a p_frame.
**
*******************************************************************************/
void rfc_set_port_state(tPORT_STATE *port_pars, MX_FRAME *p_frame)
{
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_BIT_RATE) {
port_pars->baud_rate = p_frame->u.rpn.baud_rate;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_DATA_BITS) {
port_pars->byte_size = p_frame->u.rpn.byte_size;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_STOP_BITS) {
port_pars->stop_bits = p_frame->u.rpn.stop_bits;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY) {
port_pars->parity = p_frame->u.rpn.parity;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_PARITY_TYPE) {
port_pars->parity_type = p_frame->u.rpn.parity_type;
}
if (p_frame->u.rpn.param_mask & (RFCOMM_RPN_PM_XONXOFF_ON_INPUT |
RFCOMM_RPN_PM_XONXOFF_ON_OUTPUT |
RFCOMM_RPN_PM_RTR_ON_INPUT |
RFCOMM_RPN_PM_RTR_ON_OUTPUT |
RFCOMM_RPN_PM_RTC_ON_INPUT |
RFCOMM_RPN_PM_RTC_ON_OUTPUT)) {
port_pars->fc_type = p_frame->u.rpn.fc_type;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XON_CHAR) {
port_pars->xon_char = p_frame->u.rpn.xon_char;
}
if (p_frame->u.rpn.param_mask & RFCOMM_RPN_PM_XOFF_CHAR) {
port_pars->xoff_char = p_frame->u.rpn.xoff_char;
}
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,379 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/*****************************************************************************
*
* This file contains functions callable by an application
* running on top of RFCOMM
*
*****************************************************************************/
#include <string.h>
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "stack/l2c_api.h"
#include "port_int.h"
#include "rfc_int.h"
#include "common/bt_defs.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
#if RFC_DYNAMIC_MEMORY == FALSE
tRFC_CB rfc_cb;
#else
tRFC_CB *rfc_cb_ptr;
#endif
/*******************************************************************************
**
** Function RFCOMM_StartReq
**
** Description This function handles Start Request from the upper layer.
** If RFCOMM multiplexer channel can not be allocated
** send start not accepted confirmation. Otherwise dispatch
** start event to the state machine.
**
*******************************************************************************/
void RFCOMM_StartReq (tRFC_MCB *p_mcb)
{
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_REQ, NULL);
}
/*******************************************************************************
**
** Function RFCOMM_StartRsp
**
** Description This function handles Start Response from the upper layer.
** Save upper layer handle and result of the Start Indication
** in the control block and dispatch event to the FSM.
**
*******************************************************************************/
void RFCOMM_StartRsp (tRFC_MCB *p_mcb, UINT16 result)
{
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_START_RSP, &result);
}
/*******************************************************************************
**
** Function RFCOMM_DlcEstablishReq
**
** Description This function is called by the user app to establish
** connection with the specific dlci on a specific bd device.
** It will allocate RFCOMM connection control block if not
** allocated before and dispatch open event to the state
** machine.
**
*******************************************************************************/
void RFCOMM_DlcEstablishReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu)
{
UNUSED(mtu);
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
PORT_DlcEstablishCnf (p_mcb, dlci, 0, RFCOMM_ERROR);
return;
}
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
rfc_port_sm_execute(p_port, RFC_EVENT_OPEN, NULL);
}
/*******************************************************************************
**
** Function RFCOMM_DlcEstablishRsp
**
** Description This function is called by the port emulation entity
** acks Establish Indication.
**
*******************************************************************************/
void RFCOMM_DlcEstablishRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT16 result)
{
UNUSED(mtu);
if ((p_mcb->state != RFC_MX_STATE_CONNECTED) && (result == RFCOMM_SUCCESS)) {
PORT_DlcReleaseInd (p_mcb, dlci);
return;
}
tPORT *p_port = port_find_mcb_dlci_port (p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
rfc_port_sm_execute(p_port, RFC_EVENT_ESTABLISH_RSP, &result);
}
/*******************************************************************************
**
** Function RFCOMM_ParNegReq
**
** Description This function is called by the user app to start
** DLC parameter negotiation. Port emulation can send this
** request before actually establishing the DLC. In this
** case the function will allocate RFCOMM connection control
** block.
**
*******************************************************************************/
void RFCOMM_ParNegReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu)
{
UINT8 flow;
UINT8 cl;
UINT8 k;
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
p_port->error = PORT_PAR_NEG_FAILED;
return;
}
/* Negotiate the flow control mechanism. If flow control mechanism for */
/* mux has not been set yet, use our default value. If it has been set, */
/* use that value. */
flow = (p_mcb->flow == PORT_FC_UNDEFINED) ? PORT_FC_DEFAULT : p_mcb->flow;
/* Set convergence layer and number of credits (k) */
if (flow == PORT_FC_CREDIT) {
cl = RFCOMM_PN_CONV_LAYER_CBFC_I;
k = (p_port->credit_rx_max < RFCOMM_K_MAX) ? p_port->credit_rx_max : RFCOMM_K_MAX;
p_port->credit_rx = k;
} else {
cl = RFCOMM_PN_CONV_LAYER_TYPE_1;
k = 0;
}
/* Send Parameter Negotiation Command UIH frame */
p_port->rfc.expected_rsp |= RFC_RSP_PN;
rfc_send_pn (p_mcb, dlci, TRUE, mtu, cl, k);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
}
/*******************************************************************************
**
** Function RFCOMM_ParNegRsp
**
** Description This function is called by the user app to acknowledge
** DLC parameter negotiation.
**
*******************************************************************************/
void RFCOMM_ParNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, UINT16 mtu, UINT8 cl, UINT8 k)
{
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
return;
}
/* Send Parameter Negotiation Response UIH frame */
rfc_send_pn (p_mcb, dlci, FALSE, mtu, cl, k);
}
/*******************************************************************************
**
** Function RFCOMM_PortNegReq
**
** Description This function is called by the user app to start
** Remote Port parameter negotiation. Port emulation can
** send this request before actually establishing the DLC.
** In this case the function will allocate RFCOMM connection
** control block.
**
*******************************************************************************/
void RFCOMM_PortNegReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars)
{
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
PORT_PortNegCnf (p_mcb, dlci, NULL, RFCOMM_ERROR);
return;
}
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
/* Send Parameter Negotiation Command UIH frame */
if (!p_pars) {
p_port->rfc.expected_rsp |= RFC_RSP_RPN_REPLY;
} else {
p_port->rfc.expected_rsp |= RFC_RSP_RPN;
}
rfc_send_rpn (p_mcb, dlci, TRUE, p_pars, RFCOMM_RPN_PM_MASK);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
}
/*******************************************************************************
**
** Function RFCOMM_PortNegRsp
**
** Description This function is called by the user app to acknowledge
** Port parameters negotiation.
**
*******************************************************************************/
void RFCOMM_PortNegRsp (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_STATE *p_pars,
UINT16 param_mask)
{
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
return;
}
rfc_send_rpn (p_mcb, dlci, FALSE, p_pars, param_mask);
}
/*******************************************************************************
**
** Function RFCOMM_ControlReq
**
** Description This function is called by the port entity to send control
** parameters to remote port emulation entity.
**
*******************************************************************************/
void RFCOMM_ControlReq (tRFC_MCB *p_mcb, UINT8 dlci, tPORT_CTRL *p_pars)
{
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
if ((p_port->state != PORT_STATE_OPENED)
|| (p_port->rfc.state != RFC_STATE_OPENED)) {
return;
}
p_port->port_ctrl |= PORT_CTRL_REQ_SENT;
p_port->rfc.expected_rsp |= RFC_RSP_MSC;
rfc_send_msc (p_mcb, dlci, TRUE, p_pars);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
}
/*******************************************************************************
**
** Function RFCOMM_FlowReq
**
** Description This function is called by the port entity when flow
** control state has changed. Enable flag passed shows if
** port can accept more data.
**
*******************************************************************************/
void RFCOMM_FlowReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 enable)
{
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
if ((p_port->state != PORT_STATE_OPENED)
|| (p_port->rfc.state != RFC_STATE_OPENED)) {
return;
}
p_port->local_ctrl.fc = !enable;
p_port->rfc.expected_rsp |= RFC_RSP_MSC;
rfc_send_msc (p_mcb, dlci, TRUE, &p_port->local_ctrl);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT) ;
}
/*******************************************************************************
**
** Function RFCOMM_LineStatusReq
**
** Description This function is called by the port entity when line
** status should be delivered to the peer.
**
*******************************************************************************/
void RFCOMM_LineStatusReq (tRFC_MCB *p_mcb, UINT8 dlci, UINT8 status)
{
tPORT *p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if (p_port == NULL) {
RFCOMM_TRACE_WARNING("%s Unable to find DLCI port dlci:%d", __func__,
dlci);
return;
}
if ((p_port->state != PORT_STATE_OPENED)
|| (p_port->rfc.state != RFC_STATE_OPENED)) {
return;
}
p_port->rfc.expected_rsp |= RFC_RSP_RLS;
rfc_send_rls (p_mcb, dlci, TRUE, status);
rfc_port_timer_start (p_port, RFC_T2_TIMEOUT);
}
/*******************************************************************************
**
** Function RFCOMM_DlcReleaseReq
**
** Description This function is called by the PORT unit to close DLC
**
*******************************************************************************/
void RFCOMM_DlcReleaseReq (tRFC_MCB *p_mcb, UINT8 dlci)
{
rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_CLOSE, 0);
}
/*******************************************************************************
**
** Function RFCOMM_DataReq
**
** Description This function is called by the user app to send data buffer
**
*******************************************************************************/
void RFCOMM_DataReq (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf)
{
rfc_port_sm_execute(port_find_mcb_dlci_port (p_mcb, dlci), RFC_EVENT_DATA, p_buf);
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,953 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/******************************************************************************
*
* This file contains functions to send TS 07.10 frames
*
******************************************************************************/
#include <stddef.h>
#include <string.h>
#include "common/bt_target.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "stack/l2c_api.h"
#include "port_int.h"
#include "rfc_int.h"
#include "osi/mutex.h"
#include "osi/allocator.h"
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
/*******************************************************************************
**
** Function rfc_send_sabme
**
** Description This function sends SABME frame.
**
*******************************************************************************/
void rfc_send_sabme (tRFC_MCB *p_mcb, UINT8 dlci)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE);
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
/* SABME frame, command, PF = 1, dlci */
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_SABME | RFCOMM_PF;
*p_data++ = RFCOMM_EA | 0;
*p_data = RFCOMM_SABME_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci);
p_buf->len = 4;
rfc_check_send_cmd(p_mcb, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_ua
**
** Description This function sends UA frame.
**
*******************************************************************************/
void rfc_send_ua (tRFC_MCB *p_mcb, UINT8 dlci)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE);
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
/* ua frame, response, PF = 1, dlci */
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_UA | RFCOMM_PF;
*p_data++ = RFCOMM_EA | 0;
*p_data = RFCOMM_UA_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci);
p_buf->len = 4;
rfc_check_send_cmd(p_mcb, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_dm
**
** Description This function sends DM frame.
**
*******************************************************************************/
void rfc_send_dm (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN pf)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, FALSE);
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
/* DM frame, response, PF = 1, dlci */
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_DM | ((pf) ? RFCOMM_PF : 0);
*p_data++ = RFCOMM_EA | 0;
*p_data = RFCOMM_DM_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci);
p_buf->len = 4;
rfc_check_send_cmd(p_mcb, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_disc
**
** Description This function sends DISC frame.
**
*******************************************************************************/
void rfc_send_disc (tRFC_MCB *p_mcb, UINT8 dlci)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE);
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_data = (UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET;
/* DISC frame, command, PF = 1, dlci */
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_DISC | RFCOMM_PF;
*p_data++ = RFCOMM_EA | 0;
*p_data = RFCOMM_DISC_FCS ((UINT8 *)(p_buf + 1) + L2CAP_MIN_OFFSET, cr, dlci);
p_buf->len = 4;
rfc_check_send_cmd(p_mcb, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_buf_uih
**
** Description This function sends UIH frame.
**
*******************************************************************************/
void rfc_send_buf_uih (tRFC_MCB *p_mcb, UINT8 dlci, BT_HDR *p_buf)
{
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE);
UINT8 credits;
p_buf->offset -= RFCOMM_CTRL_FRAME_LEN;
if (p_buf->len > 127) {
p_buf->offset--;
}
if (dlci) {
credits = (UINT8)p_buf->layer_specific;
} else {
credits = 0;
}
if (credits) {
p_buf->offset--;
}
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
/* UIH frame, command, PF = 0, dlci */
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_UIH | ((credits) ? RFCOMM_PF : 0);
if (p_buf->len <= 127) {
*p_data++ = RFCOMM_EA | (p_buf->len << 1);
p_buf->len += 3;
} else {
*p_data++ = (p_buf->len & 0x7f) << 1;
*p_data++ = p_buf->len >> RFCOMM_SHIFT_LENGTH2;
p_buf->len += 4;
}
if (credits) {
*p_data++ = credits;
p_buf->len++;
}
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset + p_buf->len++;
*p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci);
if (dlci == RFCOMM_MX_DLCI) {
rfc_check_send_cmd(p_mcb, p_buf);
} else {
L2CA_DataWrite (p_mcb->lcid, p_buf);
}
}
/*******************************************************************************
**
** Function rfc_send_pn
**
** Description This function sends DLC Parameters Negotiation Frame.
**
*******************************************************************************/
void rfc_send_pn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT16 mtu, UINT8 cl, UINT8 k)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_PN;
*p_data++ = RFCOMM_EA | (RFCOMM_MX_PN_LEN << 1);
*p_data++ = dlci;
*p_data++ = RFCOMM_PN_FRAM_TYPE_UIH | cl;
/* It appeared that we need to reply with the same priority bits as we received.
** We will use the fact that we reply in the same context so rx_frame can still be used.
*/
if (is_command) {
*p_data++ = RFCOMM_PN_PRIORITY_0;
} else {
*p_data++ = rfc_cb.rfc.rx_frame.u.pn.priority;
}
*p_data++ = RFCOMM_T1_DSEC;
*p_data++ = mtu & 0xFF;
*p_data++ = mtu >> 8;
*p_data++ = RFCOMM_N2;
*p_data = k;
/* Total length is sizeof PN data + mx header 2 */
p_buf->len = RFCOMM_MX_PN_LEN + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_fcon
**
** Description This function sends Flow Control On Command.
**
*******************************************************************************/
void rfc_send_fcon (tRFC_MCB *p_mcb, BOOLEAN is_command)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCON;
*p_data++ = RFCOMM_EA | (RFCOMM_MX_FCON_LEN << 1);
/* Total length is sizeof FCON data + mx header 2 */
p_buf->len = RFCOMM_MX_FCON_LEN + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_fcoff
**
** Description This function sends Flow Control Off Command.
**
*******************************************************************************/
void rfc_send_fcoff (tRFC_MCB *p_mcb, BOOLEAN is_command)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_FCOFF;
*p_data++ = RFCOMM_EA | (RFCOMM_MX_FCOFF_LEN << 1);
/* Total length is sizeof FCOFF data + mx header 2 */
p_buf->len = RFCOMM_MX_FCOFF_LEN + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_msc
**
** Description This function sends Modem Status Command Frame.
**
*******************************************************************************/
void rfc_send_msc (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command,
tPORT_CTRL *p_pars)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 signals;
UINT8 break_duration;
UINT8 len;
signals = p_pars->modem_signal;
break_duration = p_pars->break_signal;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
if (break_duration) {
len = RFCOMM_MX_MSC_LEN_WITH_BREAK;
} else {
len = RFCOMM_MX_MSC_LEN_NO_BREAK;
}
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_MSC;
*p_data++ = RFCOMM_EA | (len << 1);
*p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_EA |
((p_pars->fc) ? RFCOMM_MSC_FC : 0) |
((signals & MODEM_SIGNAL_DTRDSR) ? RFCOMM_MSC_RTC : 0) |
((signals & MODEM_SIGNAL_RTSCTS) ? RFCOMM_MSC_RTR : 0) |
((signals & MODEM_SIGNAL_RI) ? RFCOMM_MSC_IC : 0) |
((signals & MODEM_SIGNAL_DCD) ? RFCOMM_MSC_DV : 0);
if (break_duration) {
*p_data++ = RFCOMM_EA | RFCOMM_MSC_BREAK_PRESENT_MASK |
(break_duration << RFCOMM_MSC_SHIFT_BREAK);
}
/* Total length is sizeof MSC data + mx header 2 */
p_buf->len = len + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_rls
**
** Description This function sends Remote Line Status Command Frame.
**
*******************************************************************************/
void rfc_send_rls (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command, UINT8 status)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RLS;
*p_data++ = RFCOMM_EA | (RFCOMM_MX_RLS_LEN << 1);
*p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_RLS_ERROR | status;
/* Total length is sizeof RLS data + mx header 2 */
p_buf->len = RFCOMM_MX_RLS_LEN + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_nsc
**
** Description This function sends Non Supported Command Response.
**
*******************************************************************************/
void rfc_send_nsc (tRFC_MCB *p_mcb)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(FALSE) | RFCOMM_MX_NSC;
*p_data++ = RFCOMM_EA | (RFCOMM_MX_NSC_LEN << 1);
*p_data++ = rfc_cb.rfc.rx_frame.ea |
(rfc_cb.rfc.rx_frame.cr << RFCOMM_SHIFT_CR) |
rfc_cb.rfc.rx_frame.type;
/* Total length is sizeof NSC data + mx header 2 */
p_buf->len = RFCOMM_MX_NSC_LEN + 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_rpn
**
** Description This function sends Remote Port Negotiation Command
**
*******************************************************************************/
void rfc_send_rpn (tRFC_MCB *p_mcb, UINT8 dlci, BOOLEAN is_command,
tPORT_STATE *p_pars, UINT16 mask)
{
BT_HDR *p_buf;
UINT8 *p_data;
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_CTRL_FRAME_LEN;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_RPN;
if (!p_pars) {
*p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_REQ_LEN << 1);
*p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI);
p_buf->len = RFCOMM_MX_RPN_REQ_LEN + 2;
} else {
*p_data++ = RFCOMM_EA | (RFCOMM_MX_RPN_LEN << 1);
*p_data++ = RFCOMM_EA | RFCOMM_CR_MASK | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = p_pars->baud_rate;
*p_data++ = (p_pars->byte_size << RFCOMM_RPN_BITS_SHIFT)
| (p_pars->stop_bits << RFCOMM_RPN_STOP_BITS_SHIFT)
| (p_pars->parity << RFCOMM_RPN_PARITY_SHIFT)
| (p_pars->parity_type << RFCOMM_RPN_PARITY_TYPE_SHIFT);
*p_data++ = p_pars->fc_type;
*p_data++ = p_pars->xon_char;
*p_data++ = p_pars->xoff_char;
*p_data++ = (mask & 0xFF);
*p_data++ = (mask >> 8);
/* Total length is sizeof RPN data + mx header 2 */
p_buf->len = RFCOMM_MX_RPN_LEN + 2;
}
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
#if BT_RFCOMM_BQB_INCLUDED
/*******************************************************************************
**
** Function rfc_bqb_send_msc_cmd
**
** Description This function sends msc command for BQB test.
**
*******************************************************************************/
void rfc_bqb_send_msc_cmd(BD_ADDR cert_pts_addr)
{
UINT8 i;
UINT8 dlci;
BOOLEAN get_dlci = FALSE;
tPORT *p_port;
tPORT_CTRL *p_pars;
tRFC_MCB *p_mcb;
if ((p_pars = (tPORT_CTRL *)osi_malloc(sizeof(tPORT_CTRL))) == NULL) {
return;
}
p_pars->modem_signal = 0;
p_pars->break_signal = 0;
p_pars->fc = TRUE;
p_mcb = port_find_mcb (cert_pts_addr);
for (i = 0; i < MAX_RFC_PORTS; i++) {
p_port = &rfc_cb.port.port[i];
if (p_port->in_use && !memcmp (p_port->bd_addr, cert_pts_addr, BD_ADDR_LEN)) {
dlci = p_port->dlci;
get_dlci = TRUE;
break;
}
}
if (get_dlci) {
rfc_send_msc(p_mcb, dlci, TRUE, p_pars);
} else {
RFCOMM_TRACE_ERROR ("Get dlci fail");
}
osi_free(p_pars);
}
#endif /* BT_RFCOMM_BQB_INCLUDED */
/*******************************************************************************
**
** Function rfc_send_test
**
** Description This function sends Test frame.
**
*******************************************************************************/
void rfc_send_test (tRFC_MCB *p_mcb, BOOLEAN is_command, BT_HDR *p_buf)
{
UINT8 *p_data;
UINT16 xx;
UINT8 *p_src, *p_dest;
BT_HDR *p_buf_new;
if ((p_buf_new = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
memcpy(p_buf_new, p_buf, sizeof(BT_HDR) + p_buf->offset + p_buf->len);
osi_free(p_buf);
p_buf = p_buf_new;
/* Shift buffer to give space for header */
if (p_buf->offset < (L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2)) {
p_src = (UINT8 *) (p_buf + 1) + p_buf->offset + p_buf->len - 1;
p_dest = (UINT8 *) (p_buf + 1) + L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2 + p_buf->len - 1;
for (xx = 0; xx < p_buf->len; xx++) {
*p_dest-- = *p_src--;
}
p_buf->offset = L2CAP_MIN_OFFSET + RFCOMM_MIN_OFFSET + 2;
}
/* Adjust offset by number of bytes we are going to fill */
p_buf->offset -= 2;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | RFCOMM_I_CR(is_command) | RFCOMM_MX_TEST;
*p_data++ = RFCOMM_EA | (p_buf->len << 1);
p_buf->len += 2;
rfc_send_buf_uih (p_mcb, RFCOMM_MX_DLCI, p_buf);
}
/*******************************************************************************
**
** Function rfc_send_credit
**
** Description This function sends a flow control credit in UIH frame.
**
*******************************************************************************/
void rfc_send_credit(tRFC_MCB *p_mcb, UINT8 dlci, UINT8 credit)
{
BT_HDR *p_buf;
UINT8 *p_data;
UINT8 cr = RFCOMM_CR(p_mcb->is_initiator, TRUE);
if ((p_buf = (BT_HDR *)osi_malloc(RFCOMM_CMD_BUF_SIZE)) == NULL) {
return;
}
p_buf->offset = L2CAP_MIN_OFFSET;
p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
*p_data++ = RFCOMM_EA | cr | (dlci << RFCOMM_SHIFT_DLCI);
*p_data++ = RFCOMM_UIH | RFCOMM_PF;
*p_data++ = RFCOMM_EA | 0;
*p_data++ = credit;
*p_data = RFCOMM_UIH_FCS ((UINT8 *)(p_buf + 1) + p_buf->offset, dlci);
p_buf->len = 5;
rfc_check_send_cmd(p_mcb, p_buf);
}
/*******************************************************************************
**
** Function rfc_parse_data
**
** Description This function processes data packet received from L2CAP
**
*******************************************************************************/
UINT8 rfc_parse_data (tRFC_MCB *p_mcb, MX_FRAME *p_frame, BT_HDR *p_buf)
{
UINT8 ead, eal, fcs;
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
UINT8 *p_start = p_data;
UINT16 len;
if (p_buf->len < RFCOMM_CTRL_FRAME_LEN) {
RFCOMM_TRACE_ERROR ("Bad Length1: %d", p_buf->len);
return (RFC_EVENT_BAD_FRAME);
}
RFCOMM_PARSE_CTRL_FIELD (ead, p_frame->cr, p_frame->dlci, p_data);
if ( !ead ) {
RFCOMM_TRACE_ERROR ("Bad Address(EA must be 1)");
return (RFC_EVENT_BAD_FRAME);
}
RFCOMM_PARSE_TYPE_FIELD (p_frame->type, p_frame->pf, p_data);
RFCOMM_PARSE_LEN_FIELD (eal, len, p_data);
p_buf->len -= (3 + !ead + !eal + 1); /* Additional 1 for FCS */
p_buf->offset += (3 + !ead + !eal);
/* handle credit if credit based flow control */
if ((p_mcb->flow == PORT_FC_CREDIT) && (p_frame->type == RFCOMM_UIH) &&
(p_frame->dlci != RFCOMM_MX_DLCI) && (p_frame->pf == 1)) {
p_frame->credit = *p_data++;
p_buf->len--;
p_buf->offset++;
} else {
p_frame->credit = 0;
}
if (p_buf->len != len) {
RFCOMM_TRACE_ERROR ("Bad Length2 %d %d", p_buf->len, len);
return (RFC_EVENT_BAD_FRAME);
}
fcs = *(p_data + len);
/* All control frames that we are sending are sent with P=1, expect */
/* reply with F=1 */
/* According to TS 07.10 spec ivalid frames are discarded without */
/* notification to the sender */
switch (p_frame->type) {
case RFCOMM_SABME:
if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)
|| !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci)
|| !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) {
RFCOMM_TRACE_ERROR ("Bad SABME");
return (RFC_EVENT_BAD_FRAME);
} else {
return (RFC_EVENT_SABME);
}
case RFCOMM_UA:
if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr)
|| !p_frame->pf || len || !RFCOMM_VALID_DLCI (p_frame->dlci)
|| !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) {
RFCOMM_TRACE_ERROR ("Bad UA");
return (RFC_EVENT_BAD_FRAME);
} else {
return (RFC_EVENT_UA);
}
case RFCOMM_DM:
if (RFCOMM_FRAME_IS_CMD(p_mcb->is_initiator, p_frame->cr)
|| len || !RFCOMM_VALID_DLCI(p_frame->dlci)
|| !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) {
RFCOMM_TRACE_ERROR ("Bad DM");
return (RFC_EVENT_BAD_FRAME);
} else {
return (RFC_EVENT_DM);
}
case RFCOMM_DISC:
if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)
|| !p_frame->pf || len || !RFCOMM_VALID_DLCI(p_frame->dlci)
|| !rfc_check_fcs (RFCOMM_CTRL_FRAME_LEN, p_start, fcs)) {
RFCOMM_TRACE_ERROR ("Bad DISC");
return (RFC_EVENT_BAD_FRAME);
} else {
return (RFC_EVENT_DISC);
}
case RFCOMM_UIH:
if (!RFCOMM_VALID_DLCI(p_frame->dlci)) {
RFCOMM_TRACE_ERROR ("Bad UIH - invalid DLCI");
return (RFC_EVENT_BAD_FRAME);
} else if (!rfc_check_fcs (2, p_start, fcs)) {
RFCOMM_TRACE_ERROR ("Bad UIH - FCS");
return (RFC_EVENT_BAD_FRAME);
} else if (RFCOMM_FRAME_IS_RSP(p_mcb->is_initiator, p_frame->cr)) {
/* we assume that this is ok to allow bad implementations to work */
RFCOMM_TRACE_ERROR ("Bad UIH - response");
return (RFC_EVENT_UIH);
} else {
return (RFC_EVENT_UIH);
}
}
return (RFC_EVENT_BAD_FRAME);
}
/*******************************************************************************
**
** Function rfc_process_mx_message
**
** Description This function processes UIH frames received on the
** multiplexer control channel.
**
*******************************************************************************/
void rfc_process_mx_message (tRFC_MCB *p_mcb, BT_HDR *p_buf)
{
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
MX_FRAME *p_rx_frame = &rfc_cb.rfc.rx_frame;
UINT16 length = p_buf->len;
UINT8 ea, cr, mx_len;
BOOLEAN is_command;
p_rx_frame->ea = *p_data & RFCOMM_EA;
p_rx_frame->cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
p_rx_frame->type = *p_data++ & ~(RFCOMM_CR_MASK | RFCOMM_EA_MASK);
if (!p_rx_frame->ea || !length) {
RFCOMM_TRACE_ERROR ("Illegal MX Frame ea:%d len:%d", p_rx_frame->ea, length);
osi_free (p_buf);
return;
}
length--;
is_command = p_rx_frame->cr;
ea = *p_data & RFCOMM_EA;
mx_len = *p_data++ >> RFCOMM_SHIFT_LENGTH1;
length--;
if (!ea) {
mx_len += *p_data++ << RFCOMM_SHIFT_LENGTH2;
length --;
}
if (mx_len != length) {
RFCOMM_TRACE_ERROR ("Bad MX frame");
osi_free (p_buf);
return;
}
switch (p_rx_frame->type) {
case RFCOMM_MX_PN:
if (length != RFCOMM_MX_PN_LEN) {
break;
}
p_rx_frame->dlci = *p_data++ & RFCOMM_PN_DLCI_MASK;
p_rx_frame->u.pn.frame_type = *p_data & RFCOMM_PN_FRAME_TYPE_MASK;
p_rx_frame->u.pn.conv_layer = *p_data++ & RFCOMM_PN_CONV_LAYER_MASK;
p_rx_frame->u.pn.priority = *p_data++ & RFCOMM_PN_PRIORITY_MASK;
p_rx_frame->u.pn.t1 = *p_data++;
p_rx_frame->u.pn.mtu = *p_data + (*(p_data + 1) << 8);
p_data += 2;
p_rx_frame->u.pn.n2 = *p_data++;
p_rx_frame->u.pn.k = *p_data++ & RFCOMM_PN_K_MASK;
if (!p_rx_frame->dlci
|| !RFCOMM_VALID_DLCI (p_rx_frame->dlci)
|| (p_rx_frame->u.pn.mtu < RFCOMM_MIN_MTU)
|| (p_rx_frame->u.pn.mtu > RFCOMM_MAX_MTU)) {
RFCOMM_TRACE_ERROR ("Bad PN frame");
break;
}
osi_free (p_buf);
rfc_process_pn (p_mcb, is_command, p_rx_frame);
return;
case RFCOMM_MX_TEST:
if (!length) {
break;
}
p_rx_frame->u.test.p_data = p_data;
p_rx_frame->u.test.data_len = length;
p_buf->offset += 2;
p_buf->len -= 2;
if (is_command) {
rfc_send_test (p_mcb, FALSE, p_buf);
} else {
rfc_process_test_rsp (p_mcb, p_buf);
}
return;
case RFCOMM_MX_FCON:
if (length != RFCOMM_MX_FCON_LEN) {
break;
}
osi_free (p_buf);
rfc_process_fcon (p_mcb, is_command);
return;
case RFCOMM_MX_FCOFF:
if (length != RFCOMM_MX_FCOFF_LEN) {
break;
}
osi_free (p_buf);
rfc_process_fcoff (p_mcb, is_command);
return;
case RFCOMM_MX_MSC:
ea = *p_data & RFCOMM_EA;
cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI;
if (!ea || !cr || !p_rx_frame->dlci
|| !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) {
RFCOMM_TRACE_ERROR ("Bad MSC frame");
break;
}
p_rx_frame->u.msc.signals = *p_data++;
if (mx_len == RFCOMM_MX_MSC_LEN_WITH_BREAK) {
p_rx_frame->u.msc.break_present = *p_data & RFCOMM_MSC_BREAK_PRESENT_MASK;
p_rx_frame->u.msc.break_duration = (*p_data & RFCOMM_MSC_BREAK_MASK) >> RFCOMM_MSC_SHIFT_BREAK;
} else {
p_rx_frame->u.msc.break_present = FALSE;
p_rx_frame->u.msc.break_duration = 0;
}
osi_free (p_buf);
rfc_process_msc (p_mcb, is_command, p_rx_frame);
return;
case RFCOMM_MX_NSC:
if ((length != RFCOMM_MX_NSC_LEN) || !is_command) {
break;
}
p_rx_frame->u.nsc.ea = *p_data & RFCOMM_EA;
p_rx_frame->u.nsc.cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
p_rx_frame->u.nsc.type = *p_data++ >> RFCOMM_SHIFT_DLCI;
osi_free (p_buf);
rfc_process_nsc (p_mcb, p_rx_frame);
return;
case RFCOMM_MX_RPN:
if ((length != RFCOMM_MX_RPN_REQ_LEN) && (length != RFCOMM_MX_RPN_LEN)) {
break;
}
ea = *p_data & RFCOMM_EA;
cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI;
if (!ea || !cr || !p_rx_frame->dlci
|| !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) {
RFCOMM_TRACE_ERROR ("Bad RPN frame");
break;
}
p_rx_frame->u.rpn.is_request = (length == RFCOMM_MX_RPN_REQ_LEN);
if (!p_rx_frame->u.rpn.is_request) {
p_rx_frame->u.rpn.baud_rate = *p_data++;
p_rx_frame->u.rpn.byte_size = (*p_data >> RFCOMM_RPN_BITS_SHIFT) & RFCOMM_RPN_BITS_MASK;
p_rx_frame->u.rpn.stop_bits = (*p_data >> RFCOMM_RPN_STOP_BITS_SHIFT) & RFCOMM_RPN_STOP_BITS_MASK;
p_rx_frame->u.rpn.parity = (*p_data >> RFCOMM_RPN_PARITY_SHIFT) & RFCOMM_RPN_PARITY_MASK;
p_rx_frame->u.rpn.parity_type = (*p_data++ >> RFCOMM_RPN_PARITY_TYPE_SHIFT) & RFCOMM_RPN_PARITY_TYPE_MASK;
p_rx_frame->u.rpn.fc_type = *p_data++ & RFCOMM_FC_MASK;
p_rx_frame->u.rpn.xon_char = *p_data++;
p_rx_frame->u.rpn.xoff_char = *p_data++;
p_rx_frame->u.rpn.param_mask = (*p_data + (*(p_data + 1) << 8)) & RFCOMM_RPN_PM_MASK;
}
osi_free (p_buf);
rfc_process_rpn (p_mcb, is_command, p_rx_frame->u.rpn.is_request, p_rx_frame);
return;
case RFCOMM_MX_RLS:
if (length != RFCOMM_MX_RLS_LEN) {
break;
}
ea = *p_data & RFCOMM_EA;
cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
p_rx_frame->dlci = *p_data++ >> RFCOMM_SHIFT_DLCI;
p_rx_frame->u.rls.line_status = (*p_data & ~0x01);
if (!ea || !cr || !p_rx_frame->dlci
|| !RFCOMM_VALID_DLCI (p_rx_frame->dlci)) {
RFCOMM_TRACE_ERROR ("Bad RPN frame");
break;
}
osi_free (p_buf);
rfc_process_rls (p_mcb, is_command, p_rx_frame);
return;
}
osi_free (p_buf);
if (is_command) {
rfc_send_nsc (p_mcb);
}
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
@@ -0,0 +1,510 @@
/******************************************************************************
*
* Copyright (C) 1999-2012 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
/*****************************************************************************
*
* This file contains collection of utility functions used the RFCOMM unit
*
*****************************************************************************/
#include "common/bt_target.h"
#include "stack/btm_api.h"
#include "btm_int.h"
#include "stack/rfcdefs.h"
#include "stack/port_api.h"
#include "stack/port_ext.h"
#include "port_int.h"
#include "rfc_int.h"
#include "stack/btu.h"
#include "common/bt_defs.h"
#include "osi/allocator.h"
#include "osi/mutex.h"
#include <string.h>
#if (defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)
/*******************************************************************************
**
** Function rfc_calc_fcs
**
** Description Reversed CRC Table , 8-bit, poly=0x07
** (GSM 07.10 TS 101 369 V6.3.0)
*******************************************************************************/
static const UINT8 rfc_crctable[] = {
0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
};
/*******************************************************************************
**
** Function rfc_calc_fcs
**
** Description This function calculate FCS for the RFCOMM frame
** (GSM 07.10 TS 101 369 V6.3.0)
**
** Input len - number of bytes in the message
** p - points to message
**
*******************************************************************************/
UINT8 rfc_calc_fcs (UINT16 len, UINT8 *p)
{
UINT8 fcs = 0xFF;
while (len--) {
fcs = rfc_crctable[fcs ^ *p++];
}
/* Ones compliment */
return (0xFF - fcs);
}
/*******************************************************************************
**
** Function rfc_check_fcs
**
** Description This function checks FCS for the RFCOMM frame
** (GSM 07.10 TS 101 369 V6.3.0)
**
** Input len - number of bytes in the message
** p - points to message
** received_fcs - received FCS
**
*******************************************************************************/
BOOLEAN rfc_check_fcs (UINT16 len, UINT8 *p, UINT8 received_fcs)
{
UINT8 fcs = 0xFF;
while (len--) {
fcs = rfc_crctable[fcs ^ *p++];
}
/* Ones compliment */
fcs = rfc_crctable[fcs ^ received_fcs];
/*0xCF is the reversed order of 11110011.*/
return (fcs == 0xCF);
}
/*******************************************************************************
**
** Function rfc_alloc_multiplexer_channel
**
** Description This function returns existing or new control block for
** the BD_ADDR.
**
*******************************************************************************/
tRFC_MCB *rfc_alloc_multiplexer_channel (BD_ADDR bd_addr, BOOLEAN is_initiator)
{
int i, j;
tRFC_MCB *p_mcb = NULL;
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel: bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d", is_initiator);
for (i = 0; i < MAX_BD_CONNECTIONS; i++) {
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel rfc_cb.port.rfc_mcb[%d].state:%d",
i, rfc_cb.port.rfc_mcb[i].state);
RFCOMM_TRACE_DEBUG("(rfc_cb.port.rfc_mcb[i].bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
rfc_cb.port.rfc_mcb[i].bd_addr[0], rfc_cb.port.rfc_mcb[i].bd_addr[1],
rfc_cb.port.rfc_mcb[i].bd_addr[2], rfc_cb.port.rfc_mcb[i].bd_addr[3],
rfc_cb.port.rfc_mcb[i].bd_addr[4], rfc_cb.port.rfc_mcb[i].bd_addr[5]);
if ((rfc_cb.port.rfc_mcb[i].state != RFC_MX_STATE_IDLE)
&& (!memcmp (rfc_cb.port.rfc_mcb[i].bd_addr, bd_addr, BD_ADDR_LEN))) {
/* Multiplexer channel found do not change anything */
/* If there was an inactivity timer running stop it now */
if (rfc_cb.port.rfc_mcb[i].state == RFC_MX_STATE_CONNECTED) {
rfc_timer_stop (&rfc_cb.port.rfc_mcb[i]);
}
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, found, state:%d, p_mcb:%p",
is_initiator, rfc_cb.port.rfc_mcb[i].state, &rfc_cb.port.rfc_mcb[i]);
return (&rfc_cb.port.rfc_mcb[i]);
}
}
/* connection with bd_addr does not exist */
for (i = 0, j = rfc_cb.rfc.last_mux + 1; i < MAX_BD_CONNECTIONS; i++, j++) {
if (j >= MAX_BD_CONNECTIONS) {
j = 0;
}
p_mcb = &rfc_cb.port.rfc_mcb[j];
if (rfc_cb.port.rfc_mcb[j].state == RFC_MX_STATE_IDLE) {
/* New multiplexer control block */
fixed_queue_free(p_mcb->cmd_q, NULL);
rfc_timer_free(p_mcb);
memset (p_mcb, 0, sizeof (tRFC_MCB));
memcpy (p_mcb->bd_addr, bd_addr, BD_ADDR_LEN);
RFCOMM_TRACE_DEBUG("rfc_alloc_multiplexer_channel:is_initiator:%d, create new p_mcb:%p, index:%d",
is_initiator, &rfc_cb.port.rfc_mcb[j], j);
p_mcb->cmd_q = fixed_queue_new(QUEUE_SIZE_MAX);
p_mcb->is_initiator = is_initiator;
rfc_timer_start (p_mcb, RFC_MCB_INIT_INACT_TIMER);
rfc_cb.rfc.last_mux = (UINT8) j;
return (p_mcb);
}
}
return (NULL);
}
void osi_free_fun(void *p)
{
osi_free(p);
}
/*******************************************************************************
**
** Function rfc_release_multiplexer_channel
**
** Description This function returns existing or new control block for
** the BD_ADDR.
**
*******************************************************************************/
void rfc_release_multiplexer_channel (tRFC_MCB *p_mcb)
{
rfc_timer_free (p_mcb);
fixed_queue_free(p_mcb->cmd_q, osi_free_fun);
memset (p_mcb, 0, sizeof (tRFC_MCB));
p_mcb->state = RFC_MX_STATE_IDLE;
}
/*******************************************************************************
**
** Function rfc_timer_start
**
** Description Start RFC Timer
**
*******************************************************************************/
void rfc_timer_start (tRFC_MCB *p_mcb, UINT16 timeout)
{
TIMER_LIST_ENT *p_tle = &p_mcb->tle;
RFCOMM_TRACE_EVENT ("rfc_timer_start - timeout:%d", timeout);
p_tle->param = (UINT32)p_mcb;
btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_MFC, timeout);
}
/*******************************************************************************
**
** 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 rfc_timer_free
**
** Description Stop and free RFC Timer
**
*******************************************************************************/
void rfc_timer_free (tRFC_MCB *p_mcb)
{
RFCOMM_TRACE_EVENT ("rfc_timer_free");
btu_free_timer (&p_mcb->tle);
memset(&p_mcb->tle, 0, sizeof(TIMER_LIST_ENT));
}
/*******************************************************************************
**
** Function rfc_port_timer_start
**
** Description Start RFC Timer
**
*******************************************************************************/
void rfc_port_timer_start (tPORT *p_port, UINT16 timeout)
{
TIMER_LIST_ENT *p_tle = &p_port->rfc.tle;
RFCOMM_TRACE_EVENT ("rfc_port_timer_start - timeout:%d", timeout);
p_tle->param = (UINT32)p_port;
btu_start_timer (p_tle, BTU_TTYPE_RFCOMM_PORT, timeout);
}
/*******************************************************************************
**
** Function rfc_port_timer_stop
**
** Description Stop RFC Timer
**
*******************************************************************************/
void rfc_port_timer_stop (tPORT *p_port)
{
RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
btu_stop_timer (&p_port->rfc.tle);
}
/*******************************************************************************
**
** Function rfc_port_timer_free
**
** Description Stop and free RFC Timer
**
*******************************************************************************/
void rfc_port_timer_free (tPORT *p_port)
{
RFCOMM_TRACE_EVENT ("rfc_port_timer_stop");
btu_free_timer (&p_port->rfc.tle);
memset(&p_port->rfc.tle, 0, sizeof(TIMER_LIST_ENT));
}
/*******************************************************************************
**
** Function rfc_check_mcb_active
**
** Description Check if there are any opened ports on the MCB if not
** start MCB Inact timer.
**
** Returns void
**
*******************************************************************************/
void rfc_check_mcb_active (tRFC_MCB *p_mcb)
{
UINT16 i;
for (i = 0; i < RFCOMM_MAX_DLCI; i++) {
if (p_mcb->port_inx[i] != 0) {
p_mcb->is_disc_initiator = FALSE;
return;
}
}
/* The last port was DISCed. On the client side start disconnecting Mx */
/* On the server side start inactivity timer */
if (p_mcb->is_disc_initiator) {
p_mcb->is_disc_initiator = FALSE;
rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CLOSE_REQ, NULL);
} else {
rfc_timer_start (p_mcb, RFC_MCB_RELEASE_INACT_TIMER);
}
}
/*******************************************************************************
**
** Function rfcomm_process_timeout
**
** Description The function called every second to check RFCOMM timers
**
** Returns void
**
*******************************************************************************/
void rfcomm_process_timeout (TIMER_LIST_ENT *p_tle)
{
switch (p_tle->event) {
case BTU_TTYPE_RFCOMM_MFC:
rfc_mx_sm_execute ((tRFC_MCB *)p_tle->param, RFC_EVENT_TIMEOUT, NULL);
break;
case BTU_TTYPE_RFCOMM_PORT:
rfc_port_sm_execute ((tPORT *)p_tle->param, RFC_EVENT_TIMEOUT, NULL);
break;
default:
break;
}
}
/*******************************************************************************
**
** Function rfc_sec_check_complete
**
** Description The function called when Security Manager finishes
** verification of the service side connection
**
** Returns void
**
*******************************************************************************/
void rfc_sec_check_complete (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
{
tPORT *p_port = (tPORT *)p_ref_data;
UNUSED(bd_addr);
UNUSED(transport);
/* Verify that PORT is still waiting for Security to complete */
if (!p_port->in_use
|| ((p_port->rfc.state != RFC_STATE_ORIG_WAIT_SEC_CHECK)
&& (p_port->rfc.state != RFC_STATE_TERM_WAIT_SEC_CHECK))) {
return;
}
rfc_port_sm_execute ((tPORT *)p_ref_data, RFC_EVENT_SEC_COMPLETE, &res);
}
/*******************************************************************************
**
** Function rfc_port_closed
**
** Description The function is called when port is released based on the
** event received from the lower layer, typically L2CAP
** connection down, DISC, or DM frame.
**
** Returns void
**
*******************************************************************************/
void rfc_port_closed (tPORT *p_port)
{
tRFC_MCB *p_mcb = p_port->rfc.p_mcb;
RFCOMM_TRACE_DEBUG ("rfc_port_closed");
rfc_port_timer_stop (p_port);
p_port->rfc.state = RFC_STATE_CLOSED;
/* If multiplexer channel was up mark it as down */
if (p_mcb) {
p_mcb->port_inx[p_port->dlci] = 0;
/* If there are no more ports opened on this MCB release it */
rfc_check_mcb_active (p_mcb);
}
/* Notify port that RFC connection is gone */
port_rfc_closed (p_port, PORT_CLOSED);
}
/*******************************************************************************
**
** Function rfc_inc_credit
**
** Description The function is called when a credit is received in a UIH
** frame. It increments the TX credit count, and if data
** flow had halted, it restarts it.
**
** Returns void
**
*******************************************************************************/
void rfc_inc_credit (tPORT *p_port, UINT8 credit)
{
if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
p_port->credit_tx += credit;
RFCOMM_TRACE_EVENT ("rfc_inc_credit:%d", p_port->credit_tx);
if (p_port->tx.peer_fc == TRUE) {
PORT_FlowInd(p_port->rfc.p_mcb, p_port->dlci, TRUE);
}
}
}
/*******************************************************************************
**
** Function rfc_dec_credit
**
** Description The function is called when a UIH frame of user data is
** sent. It decrements the credit count. If credit count
** Reaches zero, peer_fc is set.
**
** Returns void
**
*******************************************************************************/
void rfc_dec_credit (tPORT *p_port)
{
if (p_port->rfc.p_mcb->flow == PORT_FC_CREDIT) {
if (p_port->credit_tx > 0) {
p_port->credit_tx--;
}
if (p_port->credit_tx == 0) {
p_port->tx.peer_fc = TRUE;
}
}
}
/*******************************************************************************
**
** Function rfc_check_send_cmd
**
** Description This function is called to send an RFCOMM command message
** or to handle the RFCOMM command message queue.
**
** Returns void
**
*******************************************************************************/
void rfc_check_send_cmd(tRFC_MCB *p_mcb, BT_HDR *p_buf)
{
BT_HDR *p;
/* if passed a buffer queue it */
if (p_buf != NULL) {
if (p_mcb->cmd_q == NULL) {
RFCOMM_TRACE_ERROR("%s: empty queue: p_mcb = %p p_mcb->lcid = %u cached p_mcb = %p",
__func__, p_mcb, p_mcb->lcid,
rfc_find_lcid_mcb(p_mcb->lcid));
}
fixed_queue_enqueue(p_mcb->cmd_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT);
}
/* handle queue if L2CAP not congested */
while (p_mcb->l2cap_congested == FALSE) {
if ((p = (BT_HDR *)fixed_queue_dequeue(p_mcb->cmd_q, 0)) == NULL) {
break;
}
L2CA_DataWrite (p_mcb->lcid, p);
}
}
#endif ///(defined RFCOMM_INCLUDED && RFCOMM_INCLUDED == TRUE)