summaryrefslogtreecommitdiffstats
path: root/stack/obx/obx_sact.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/obx/obx_sact.c')
-rw-r--r--stack/obx/obx_sact.c1519
1 files changed, 1519 insertions, 0 deletions
diff --git a/stack/obx/obx_sact.c b/stack/obx/obx_sact.c
new file mode 100644
index 0000000..1602752
--- /dev/null
+++ b/stack/obx/obx_sact.c
@@ -0,0 +1,1519 @@
+/*****************************************************************************
+**
+** Name: obx_sact.c
+**
+** File: OBEX Server State Machine Action Functions
+**
+** Copyright (c) 2003-2012, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+**
+*****************************************************************************/
+#include <string.h>
+#include <bt_target.h>
+
+#include "btu.h"
+#include "obx_int.h"
+#include "btm_api.h"
+
+/*******************************************************************************
+** Function obx_sa_snd_rsp
+** Description Call p_send_fn() to send the OBEX message to the peer.
+** Start timer. Return NULL state.If data is partially sent, set
+** next_state in port control block. Return PART state.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_snd_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = p_scb->state;
+ UINT8 rsp_code = OBX_RSP_DEFAULT;
+ tOBX_SR_CB *p_cb;
+ BOOLEAN not_cong = TRUE;
+
+ obx_access_rsp_code(p_pkt, &rsp_code);
+ p_scb->cur_op = OBX_REQ_ABORT;
+
+
+ p_scb->ll_cb.comm.p_txmsg = p_pkt;
+ rsp_code &= ~OBX_FINAL;
+ /* Get and Put operation may need to adjust the state*/
+ if (rsp_code != OBX_RSP_CONTINUE )
+ {
+ if (p_scb->state == OBX_SS_GET_TRANSACTION ||
+ p_scb->state == OBX_SS_PUT_SRM ||
+ p_scb->state == OBX_SS_GET_SRM ||
+ p_scb->state == OBX_SS_PUT_TRANSACTION)
+ {
+ state = OBX_SS_CONNECTED;
+ /* the SRM bits can not be cleared here, if aborting */
+ if ((p_scb->srm &OBX_SRM_ABORT) == 0)
+ p_scb->srm &= OBX_SRM_ENABLE;
+ }
+ }
+
+ OBX_TRACE_DEBUG2("obx_sa_snd_rsp sess_st:%d, event:%d", p_scb->sess_st, p_pkt->event);
+ if ((p_scb->sess_st == OBX_SESS_ACTIVE) && (p_pkt->event != (OBX_SESSION_CFM_SEVT + 1)))
+ {
+ p_scb->ssn++;
+ }
+
+ if (p_scb->ll_cb.comm.p_send_fn(&p_scb->ll_cb) == FALSE)
+ {
+ p_scb->next_state = state;
+ state = OBX_SS_PARTIAL_SENT;
+ }
+ else if (p_scb->state == OBX_SS_GET_SRM)
+ {
+ if (((p_scb->srmp & OBX_SRMP_WAIT) == 0) && (rsp_code == OBX_RSP_CONTINUE))
+ {
+ if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg)
+ {
+ OBX_TRACE_DEBUG1("obx_sa_snd_rsp cong:%d", p_scb->ll_cb.l2c.cong);
+ if (p_scb->ll_cb.l2c.cong)
+ {
+ not_cong = FALSE;
+ }
+ }
+
+ /* do not need to wait
+ - fake a get request event, so the profile would issue another GET response */
+ if (not_cong)
+ {
+ p_cb = obx_sr_get_cb(p_scb->handle);
+ (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, NULL);
+ }
+ }
+ p_scb->srmp &= ~OBX_SRMP_WAIT;
+ }
+ else if (p_scb->sess_st == OBX_SESS_SUSPENDING)
+ {
+ p_scb->ssn++;
+ p_scb->param.sess.ssn = p_scb->ssn;
+ p_scb->param.sess.nssn = p_scb->ssn;
+ p_scb->sess_st = OBX_SESS_SUSPEND;
+ p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_scb->state;
+ state = OBX_SS_SESS_INDICATED;
+ p_scb->api_evt = OBX_SESSION_REQ_EVT;
+ }
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_snd_part
+** Description Call p_send_fn() to send the left-over OBEX message to the
+** peer. Start timer. If all the data is sent, call obx_ssm_event()
+** with STATE event to next_state in the port control block.
+** If (p_saved), call obx_ssm_event() to process the saved request.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_snd_part(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ UINT8 rsp_code = OBX_RSP_DEFAULT;
+ tOBX_SR_CB *p_cb;
+
+ obx_access_rsp_code(p_scb->ll_cb.comm.p_txmsg , &rsp_code);
+ if (p_scb->ll_cb.comm.p_send_fn(&p_scb->ll_cb) == TRUE)
+ {
+ obx_ssm_event(p_scb, OBX_STATE_SEVT, NULL);
+ if (p_scb->p_next_req)
+ {
+ p_pkt = p_scb->p_next_req;
+ obx_access_rsp_code(p_pkt , &rsp_code);
+ p_scb->p_next_req = NULL;
+ p_scb->api_evt = (tOBX_EVENT)p_pkt->event;
+ obx_ssm_event(p_scb, (tOBX_SR_EVENT)(p_pkt->event-1), p_pkt);
+ }
+
+ OBX_TRACE_DEBUG3("obx_sa_snd_part state:%d, srm:0x%x, rsp_code:0x%x", p_scb->state, p_scb->srm, rsp_code);
+ if (p_scb->state == OBX_SS_GET_SRM)
+ {
+ rsp_code &= ~OBX_FINAL;
+ if (((p_scb->srm & OBX_SRM_WAIT) == 0) && (rsp_code == OBX_RSP_CONTINUE))
+ {
+ /* do not need to wait
+ - fake a get request event, so the profile would issue another GET response */
+ p_cb = obx_sr_get_cb(p_scb->handle);
+ (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, NULL);
+ }
+ }
+ }
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_abort_rsp
+** Description Send an abort response.
+** If Put/Get response has not been sent yet,
+** send it before the abort response.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_abort_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ BT_HDR *p_dummy;
+ UINT8 rsp_code = OBX_RSP_CONTINUE;
+
+ if (p_scb->cur_op != OBX_REQ_ABORT && ((p_scb->srm & OBX_SRM_ENGAGE) == 0))
+ {
+ /* if we have not respond to an op yet, send a dummy response */
+ if (p_scb->cur_op == (rsp_code|OBX_REQ_PUT) )
+ rsp_code = OBX_RSP_INTRNL_SRVR_ERR;
+ p_dummy = obx_build_dummy_rsp(p_scb, rsp_code);
+ obx_sa_snd_rsp(p_scb, p_dummy);
+ }
+
+ /* clear the SRM bits; leave only the enabled bit */
+ p_scb->srm &= OBX_SRM_ENABLE;
+ state = obx_sa_snd_rsp(p_scb, p_pkt);
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_op_rsp
+** Description Send response for Put/Get when Abort request is already received
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_op_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ if (p_scb->cur_op != OBX_REQ_ABORT)
+ state = obx_sa_snd_rsp(p_scb, p_pkt);
+#if (BT_USE_TRACES == TRUE)
+ else
+ OBX_TRACE_WARNING0("OBX is not waiting for a rsp API!!");
+#endif
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_verify_target
+** Description Verify that target header or connection ID is correct.
+** Make sure that they do not both exist
+*******************************************************************************/
+static UINT8 obx_verify_target(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ UINT16 len = 0;
+ UINT8 rsp_code = OBX_RSP_SERVICE_UNAVL;
+ UINT32 conn_id = 0;
+ UINT8 *p_target = NULL;
+
+ OBX_Read4ByteHdr(p_pkt, OBX_HI_CONN_ID, &conn_id);
+/* Coverity:
+Event unchecked_value: Return value of "OBX_ReadTargetHdr" is not checked
+Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */
+/* coverity[unchecked_value] False-positive: If target headser does not exist,
+ p_target would remain the default value/NULL and len would be set to 0.
+ There's no need to check the return value of OBX_ReadTargetHdr
+*/
+ OBX_ReadTargetHdr(p_pkt, &p_target, &len, 0);
+
+ if (p_cb->target.len)
+ {
+ /* directed connection: make sure the connection ID matches */
+ if ( conn_id && conn_id == p_scb->conn_id )
+ rsp_code = OBX_RSP_OK;
+
+ if (p_cb->target.len == OBX_DEFAULT_TARGET_LEN)
+ {
+ /* the user verify target (cases like BIP that has multiple targets) */
+ rsp_code = OBX_RSP_OK;
+ }
+ else if ( len == p_cb->target.len && p_target &&
+ memcmp(p_cb->target.target, p_target, p_cb->target.len) == 0)
+ {
+ rsp_code = OBX_RSP_OK;
+ }
+ }
+ else
+ {
+ /* no target - request to inbox, like OPP */
+ if (conn_id == 0 && len == 0)
+ {
+ if (p_scb->ll_cb.comm.tx_mtu < OBX_MIN_MTU)
+ rsp_code = OBX_RSP_FORBIDDEN;
+ else
+ rsp_code = OBX_RSP_OK;
+ }
+ }
+
+ /* target header and connection ID are not supposed to exist in the same packet*/
+ if (conn_id != 0 && p_target != NULL)
+ rsp_code = OBX_RSP_BAD_REQUEST;
+
+ OBX_TRACE_DEBUG3("obx_verify_target rsp: %x, id:%x, code:%x",
+ rsp_code, conn_id, ((tOBX_RX_HDR *)(p_pkt + 1))->code);
+ if (rsp_code != OBX_RSP_OK)
+ p_pkt->event = OBX_CONNECT_RSP_EVT;
+ return rsp_code;
+}
+
+/*******************************************************************************
+** Function obx_conn_rsp
+** Description Called by OBX_ConnectRsp() and obx_sa_connect_ind() to compose
+** a connect response packet.
+*******************************************************************************/
+BT_HDR * obx_conn_rsp(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code, BT_HDR *p_pkt)
+{
+ UINT8 msg[OBX_HDR_OFFSET + OBX_MAX_CONN_HDR_EXTRA];
+ UINT8 *p = msg;
+
+ /* response packets always have the final bit set */
+ *p++ = (rsp_code | OBX_FINAL);
+ p += OBX_PKT_LEN_SIZE;
+
+ *p++ = OBX_VERSION;
+ *p++ = OBX_CONN_FLAGS;
+ UINT16_TO_BE_STREAM(p, p_scb->ll_cb.comm.rx_mtu);
+
+ /* add session sequence number, if session is active */
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ *p++ = OBX_HI_SESSION_SN;
+ *p++ = (p_scb->ssn+1);
+ }
+
+ if (p_scb->conn_id && rsp_code == OBX_RSP_OK)
+ {
+ *p++ = OBX_HI_CONN_ID;
+ UINT32_TO_BE_STREAM(p, p_scb->conn_id);
+ }
+
+ p_pkt = obx_sr_prepend_msg(p_pkt, msg, (UINT16)(p - msg) );
+
+ /* If the target is registered to server and the WHO headers not in the packet
+ * add WHO header here */
+ p_pkt->event = OBX_CONNECT_RSP_EVT;
+ if (p_cb->target.len && p_cb->target.len != OBX_DEFAULT_TARGET_LEN &&
+ OBX_CheckHdr(p_pkt, OBX_HI_WHO) == NULL)
+ {
+ OBX_AddByteStrHdr(p_pkt, OBX_HI_WHO, p_cb->target.target, p_cb->target.len);
+ /* adjust the packet len */
+ obx_adjust_packet_len(p_pkt);
+ }
+ return p_pkt;
+}
+
+/*******************************************************************************
+** Function obx_sa_wc_conn_ind
+** Description Connect Req is received when waiting for the port to close
+** Call callback with OBX_CLOSE_IND_EVT to clean up the profiles
+** then call obx_sa_connect_ind to process the connect req.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_wc_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_EVT_PARAM param;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+
+ obx_stop_timer(&p_scb->ll_cb.comm.tle);
+ memset(&param, 0, sizeof(tOBX_EVT_PARAM));
+ if (p_cb && p_cb->p_cback)
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, NULL);
+ obx_sa_connect_ind(p_scb, p_pkt);
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_connect_ind
+** Description Save peer MTU in the server control block.If the server does not
+** register authentication, call callback with OBX_CONNECT_REQ_EVT
+** and the MTU from the request message. Return NULL state.
+** If authenticate, compose an unauthorized response, and call
+** obx_sa_snd_rsp() to send it to the client. Return WAIT_AUTH state.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_connect_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ UINT8 rsp_code = OBX_RSP_SERVICE_UNAVL;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+ UINT8 *p;
+ tOBX_EVT_PARAM param; /* The event parameter. */
+
+ /* clear the SRM bits; leave only the enabled bit */
+ p_scb->srm &= OBX_SRM_ENABLE;
+
+ OBX_TRACE_DEBUG0("obx_sa_connect_ind");
+ p_scb->api_evt = OBX_NULL_EVT;
+
+ /* verify that the connect request is OK */
+ rsp_code = obx_verify_target(p_cb, p_scb, p_pkt);
+ if (rsp_code == OBX_RSP_OK)
+ {
+ if (p_cb->target.len && p_scb->conn_id == 0)
+ {
+ /* if Connection ID is used for this connection and none assigned yet,
+ * - assign one */
+ p_scb->conn_id = obx_sr_get_next_conn_id();
+ OBX_TRACE_DEBUG1(" **** obx_sr_get_next_conn_id (0x%08x)", p_scb->conn_id);
+ }
+
+ /* tx_mtu is processed in obx_sr_proc_evt() */
+ if (p_cb->p_auth)
+ {
+ /* If client challenge us first, and the server registers for authentication:
+ * remove the authentication headers and challenge it back */
+ /* send unauthorize response */
+ p_pkt = obx_unauthorize_rsp(p_cb, p_scb, p_pkt);
+ state = OBX_SS_WAIT_AUTH;
+ }
+ else
+ {
+ if (OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL)
+ {
+ /* If client challenge us first, and the server does not register for authentication:
+ * report the challenge */
+ p_scb->p_saved_msg = obx_dup_pkt(p_pkt);
+ state = OBX_SS_AUTH_INDICATED;
+ p_scb->api_evt = OBX_PASSWORD_EVT;
+ }
+ else
+ {
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ p = &p_scb->sess_info[OBX_SESSION_INFO_ID_IDX];
+ UINT32_TO_BE_STREAM(p, p_scb->conn_id);
+ param.sess.p_sess_info = p_scb->sess_info;
+ param.sess.sess_op = OBX_SESS_OP_CREATE;
+ param.sess.sess_st = p_scb->sess_st;
+ param.sess.nssn = p_scb->param.ssn;
+ param.sess.ssn = p_scb->param.ssn;
+ param.sess.obj_offset = 0;
+ p = &p_scb->sess_info[OBX_SESSION_INFO_MTU_IDX];
+ UINT16_TO_BE_STREAM(p, p_scb->param.conn.mtu);
+ memcpy(param.sess.peer_addr, p_scb->peer_addr, BD_ADDR_LEN);
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_REQ_EVT, param, NULL);
+ }
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CONNECT_REQ_EVT, p_scb->param, p_pkt);
+ }
+ }
+ }
+ else
+ {
+ /* bad target or connection ID - reject the request */
+ rsp_code |= OBX_FINAL;
+ obx_access_rsp_code(p_pkt, &rsp_code);
+ state = OBX_SS_NOT_CONNECTED;
+ }
+
+ if (state != OBX_SS_NULL && state != OBX_SS_AUTH_INDICATED)
+ {
+ /* each port has its own credit.
+ * It's very unlikely that we can be flow controlled here */
+ obx_sa_snd_rsp(p_scb, p_pkt);
+ }
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_auth_ind
+** Description Save peer MTU and the OBEX message in the server control block.
+** Call callback function with OBX_PASSWORD_EVT.
+** Note: This action function is only valid when MD5 is included
+** Leave this as a stub function to avoid altering the state machine
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_auth_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ UINT8 rsp_code;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+
+ p_scb->api_evt = OBX_NULL_EVT;
+ rsp_code = obx_verify_target(p_cb, p_scb, p_pkt);
+
+ if (rsp_code == OBX_RSP_OK)
+ {
+ /* tx_mtu is processed in obx_sr_proc_evt() */
+ if (OBX_CheckHdr(p_pkt, OBX_HI_AUTH_RSP) == NULL)
+ {
+ /* we are expecting authentication response in this state.
+ * if none is received, reject the request */
+ p_pkt = obx_unauthorize_rsp(p_cb, p_scb, p_pkt);
+ state = OBX_SS_NOT_CONNECTED;
+ }
+ else
+ {
+ /* the client sends authentication response.
+ * save a copy in the control block. Verify it when OBX_Password() is issued */
+ p_scb->p_saved_msg = obx_dup_pkt(p_pkt);
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_PASSWORD_EVT, p_scb->param, p_pkt);
+ }
+ }
+ else
+ {
+ /* bad target or connection ID - reject the request */
+ rsp_code |= OBX_FINAL;
+ obx_access_rsp_code(p_pkt, &rsp_code);
+ state = OBX_SS_NOT_CONNECTED;
+ }
+
+ if (state != OBX_SS_NULL)
+ {
+ /* each port has its own credit.
+ * It's very unlikely that we can be flow controlled here */
+ obx_sa_snd_rsp(p_scb, p_pkt);
+ }
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_connect_rsp
+** Description obx_sa_snd_rsp().If(p_saved), free the OBEX message.If OK
+** response, return NULL state.If unauthorized response, return
+** WAIT_AUTH state.If other fail response, return NOT_CONN state.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_connect_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ UINT8 rsp_code = OBX_RSP_DEFAULT;
+ tOBX_SR_STATE state = p_scb->state;
+
+ obx_access_rsp_code(p_pkt, &rsp_code);
+
+ if (p_scb->p_saved_msg)
+ {
+ GKI_freebuf(p_scb->p_saved_msg);
+ p_scb->p_saved_msg = NULL;
+ }
+
+ if ( rsp_code == (OBX_RSP_UNAUTHORIZED | OBX_FINAL) &&
+ OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL)
+ {
+ state = OBX_SS_WAIT_AUTH;
+ }
+ else if (rsp_code != (OBX_RSP_OK | OBX_FINAL) )
+ state = OBX_SS_NOT_CONNECTED;
+
+ /* each port has its own credit.
+ * It's very unlikely that we can be flow controlled here */
+ obx_sa_snd_rsp(p_scb, p_pkt);
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_connection_error
+** Description Stop timer. Reopen transport. Call callback function with
+** OBX_CLOSE_IND_EVT.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_connection_error(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_EVT_PARAM param;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+ tOBX_SR_CBACK *p_cback = NULL;
+ tOBX_SR_STATE save_state;
+
+ OBX_TRACE_DEBUG4("obx_sa_connection_error tx_mtu: %d, sess_st:%d state:%d, prev_state:%d",
+ p_scb->ll_cb.comm.tx_mtu, p_scb->sess_st, p_scb->state, p_scb->prev_state);
+
+ if (p_cb)
+ {
+ p_cback = p_cb->p_cback;
+ memset(&param, 0, sizeof(tOBX_EVT_PARAM));
+ }
+
+ /* clear buffers from previous connection */
+ obx_free_buf (&p_scb->ll_cb);
+
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ /* The transport is interrupted while a reliable session is active:
+ * report a suspend event fot application to save the information in NV */
+ save_state = p_scb->prev_state;
+ p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_scb->srm;
+ param.sess.p_sess_info = p_scb->sess_info;
+ param.sess.sess_op = OBX_SESS_OP_TRANSPORT;
+ param.sess.sess_st = p_scb->sess_st;
+ param.sess.nssn = p_scb->ssn;
+ param.sess.ssn = p_scb->ssn;
+ param.sess.obj_offset = 0;
+ param.sess.timeout = OBX_SESS_TIMEOUT_VALUE;
+ if (save_state == OBX_SS_PARTIAL_SENT)
+ save_state = p_scb->next_state;
+
+ if ((p_scb->srm & OBX_SRM_ENGAGE) == 0)
+ {
+ /* SRM is not engaged.
+ * When the session is resume, client needs to send the request first,
+ * the save ssm state may need to be adjusted */
+ if (save_state == OBX_SS_PUT_INDICATED)
+ {
+ save_state = OBX_SS_PUT_TRANSACTION;
+ }
+ else if (save_state == OBX_SS_GET_INDICATED)
+ {
+ save_state = OBX_SS_GET_TRANSACTION;
+ }
+ }
+ p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = save_state;
+ OBX_TRACE_DEBUG2("saved state:0x%x, srm:0x%x", save_state, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX]);
+ memcpy(param.sess.peer_addr, p_scb->peer_addr, BD_ADDR_LEN);
+ p_scb->sess_st = OBX_SESS_NONE;
+ if (p_cback)
+ (*p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_INFO_EVT, param, NULL);
+ }
+ else if (p_scb->sess_st == OBX_SESS_CLOSE || p_scb->state == OBX_SS_NOT_CONNECTED)
+ p_scb->sess_st = OBX_SESS_NONE;
+
+ if (p_scb->ll_cb.comm.tx_mtu != 0)
+ {
+ p_scb->ll_cb.comm.tx_mtu = 0;
+ obx_stop_timer(&p_scb->ll_cb.comm.tle);
+ p_scb->conn_id = 0;
+ if (p_cback)
+ (*p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, p_pkt);
+ }
+
+ if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg)
+ {
+ p_scb->ll_cb.comm.id = 0; /* mark this port unused. */
+ p_scb->srm &= OBX_SRM_ENABLE;
+ obx_add_port (p_scb->handle);
+ }
+
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_close_port
+** Description Close transport. Start timer.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_close_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ if (p_pkt)
+ GKI_freebuf(p_pkt);
+ p_scb->ll_cb.comm.p_close_fn(p_scb->ll_cb.comm.id);
+ obx_sr_free_scb(p_scb);
+ obx_start_timer(&p_scb->ll_cb.comm);
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_clean_port
+** Description Close transport and clean up api_evt if illegal obex message is
+** received.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_clean_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ p_scb->api_evt = OBX_NULL_EVT;
+ return obx_sa_close_port(p_scb, p_pkt);
+}
+/*******************************************************************************
+** Function obx_sa_state
+** Description change state
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_state(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ return p_scb->next_state;
+}
+
+/*******************************************************************************
+** Function obx_sa_nc_to
+** Description Timer expires in not_conn state
+** if there is existing connections -> disconnect
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_nc_to(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ BD_ADDR bd_addr;
+
+ obx_stop_timer(&p_scb->ll_cb.comm.tle);
+ /* if there is existing connections -> disconnect */
+ if (OBX_GetPeerAddr(p_scb->ll_cb.comm.handle, bd_addr) != 0)
+ {
+ p_scb->ll_cb.comm.p_close_fn(p_scb->ll_cb.comm.id);
+ p_scb->conn_id = 0;
+ /* wait for the conn err event to re-open the port */
+ }
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_save_req
+** Description When a request received from peer in PART state,
+** save the request for later processing
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_save_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ if (p_scb->p_next_req)
+ {
+ GKI_freebuf(p_scb->p_next_req);
+ }
+ p_scb->p_next_req = p_pkt;
+ p_scb->api_evt = OBX_NULL_EVT;
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_rej_req
+** Description Send bad request response when request comes in bad state
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_rej_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ UINT8 msg[OBX_HDR_OFFSET];
+ UINT8 *p = msg;
+
+ OBX_TRACE_DEBUG0( "obx_sa_rej_req" ) ;
+ if (p_pkt)
+ GKI_freebuf(p_pkt);
+
+ /* response packets always have the final bit set */
+ *p++ = (OBX_RSP_SERVICE_UNAVL | OBX_FINAL);
+ p += OBX_PKT_LEN_SIZE;
+
+ /* add session sequence number, if session is active */
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ *p++ = OBX_HI_SESSION_SN;
+ *p++ = (p_scb->ssn+1);
+ }
+
+ /* add connection ID, if needed */
+ if (p_scb->conn_id)
+ {
+ *p++ = OBX_HI_CONN_ID;
+ UINT32_TO_BE_STREAM(p, p_scb->conn_id);
+ }
+
+ p_pkt = obx_sr_prepend_msg(NULL, msg, (UINT16)(p - msg) );
+ p_pkt->event = OBX_PUT_RSP_EVT; /* any response */
+ p_scb->api_evt = OBX_NULL_EVT;
+
+ return obx_sa_snd_rsp(p_scb, p_pkt);
+}
+
+/*******************************************************************************
+** Function obx_sa_get_ind
+** Description received a GET request from the client. Check if SRM is engaged.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_get_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+
+ if ((p_scb->srm & OBX_SRM_REQING) && (p_scb->srm & OBX_SRM_ENABLE))
+ {
+ state = OBX_CS_GET_SRM;
+ }
+
+ /* the GET request does not set the final bit.
+ * Save this request, send response automatically and do not report the event */
+ if ((!p_scb->param.get.final) && ((p_scb->srmp & OBX_SRMP_NONF_EVT) == 0))
+ {
+ p_scb->srmp |= OBX_SRMP_NONF;
+ if (p_scb->p_saved_msg)
+ GKI_freebuf(p_scb->p_saved_msg);
+ p_scb->p_saved_msg = p_pkt;
+ p_scb->api_evt = OBX_NULL_EVT;
+ OBX_GetRsp(p_scb->ll_cb.comm.handle, OBX_RSP_CONTINUE, NULL);
+ }
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_merge_get_req
+** Description merge the given 2 GET request packets and return the merged result
+*******************************************************************************/
+BT_HDR * obx_merge_get_req(BT_HDR *p_pkt1, BT_HDR *p_pkt2)
+{
+ BT_HDR *p_ret = p_pkt1;
+ UINT16 size, need;
+ UINT8 *p1, *p2;
+ UINT8 *p, pre_size = OBX_GET_HDRS_OFFSET;
+
+ /* skip the connection ID header */
+ p = (UINT8 *)(p_pkt2 + 1) + p_pkt2->offset;
+ if (*p == OBX_HI_CONN_ID)
+ pre_size += 5;
+
+ need = p_pkt1->len + p_pkt2->len;
+ if ((p_pkt2->len == pre_size) || (need >= OBX_LRG_DATA_POOL_SIZE))
+ {
+ GKI_freebuf (p_pkt2);
+ return p_pkt1;
+ }
+
+ /* get rid of the GET request header - opcode(1) + packet len(2) (and maybe connection ID) before merging */
+ p_pkt2->len -= pre_size;
+ p_pkt2->offset += pre_size;
+ size = GKI_get_buf_size(p_pkt1);
+
+ if (size < need)
+ {
+ /* the original p_pkt1 is too small.
+ * Allocate a bigger GKI buffer, p_ret, and copy p_pkt1 into p_ret */
+ if (need < GKI_MAX_BUF_SIZE)
+ {
+ /* Use the largest general pool to allow challenge tags appendage */
+ p_ret = (BT_HDR *)GKI_getbuf(GKI_MAX_BUF_SIZE);
+ }
+ else
+ {
+ p_ret = (BT_HDR *) GKI_getpoolbuf(OBX_LRG_DATA_POOL_ID);
+ }
+ memcpy (p_ret, p_pkt1, sizeof (BT_HDR));
+ p_ret->offset = 0;
+ p1 = (UINT8 *)(p_ret + 1);
+ p2 = (UINT8 *)(p_pkt1 + 1) + p_pkt1->offset;
+ memcpy (p1, p2, p_pkt1->len);
+ GKI_freebuf (p_pkt1);
+ }
+
+ /* adjust the actualy packet length to reflect the combined packet and copy p_pkt2 into p_ret */
+ p1 = (UINT8 *)(p_ret + 1) + p_ret->offset + 1;
+ size = p_ret->len + p_pkt2->len;
+ UINT16_TO_BE_STREAM(p1, size);
+ p1 = (UINT8 *)(p_ret + 1) + p_ret->offset + p_ret->len;
+ p2 = (UINT8 *)(p_pkt2 + 1) + p_pkt2->offset;
+ p_ret->len = size;
+ memcpy (p1, p2, p_pkt2->len);
+ GKI_freebuf (p_pkt2);
+
+ return p_ret;
+}
+
+/*******************************************************************************
+** Function obx_sa_get_req
+** Description
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ tOBX_SR_CB *p_cb;
+
+ OBX_TRACE_DEBUG2("obx_sa_get_req srmp:0x%x final:%d", p_scb->srmp, p_scb->param.get.final);
+ if (p_scb->srmp & OBX_SRMP_NONF)
+ {
+ /* the GET request does not set the final bit yet.
+ * merge this request, send response automatically and do not report the event */
+ if (!p_scb->param.get.final)
+ {
+ p_scb->p_saved_msg = obx_merge_get_req(p_scb->p_saved_msg, p_pkt);
+ p_scb->api_evt = OBX_NULL_EVT;
+ OBX_GetRsp(p_scb->ll_cb.comm.handle, OBX_RSP_CONTINUE, NULL);
+ }
+ else
+ {
+ p_scb->srmp &= ~OBX_SRMP_NONF;
+ p_pkt = obx_merge_get_req(p_scb->p_saved_msg, p_pkt);
+ p_scb->p_saved_msg = NULL;
+ p_scb->api_evt = OBX_NULL_EVT;
+ p_cb = &obx_cb.server[p_scb->handle - 1];
+ (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, p_pkt);
+ memset(&p_scb->param, 0, sizeof (p_scb->param) );
+ }
+ }
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_make_sess_id
+** Description compute the session id
+*******************************************************************************/
+static tOBX_STATUS obx_sa_make_sess_id (tOBX_SR_SESS_CB *p_scb, UINT8 *p_sess_info,
+ tOBX_TRIPLET *p_triplet, UINT8 num_triplet)
+{
+ UINT8 data[10];
+ UINT8 ind;
+ UINT8 *p;
+
+ /* check the device address session parameter */
+ ind = obx_read_triplet(p_triplet, num_triplet, OBX_TAG_SESS_PARAM_ADDR);
+ OBX_TRACE_DEBUG2("addr ind:%d, num:%d", ind, num_triplet);
+ if (ind == num_triplet || p_triplet[ind].len != BD_ADDR_LEN)
+ {
+ OBX_TRACE_ERROR0("No Device Addr parameter");
+ return OBX_BAD_PARAMS;
+ }
+
+ if (memcmp (p_scb->peer_addr, p_triplet[ind].p_array, BD_ADDR_LEN) != 0)
+ {
+ OBX_TRACE_ERROR0("Bad Device Addr parameter");
+ return OBX_BAD_PARAMS;
+ }
+
+ /* check the nonce session parameter */
+ ind = obx_read_triplet(p_triplet, num_triplet, OBX_TAG_SESS_PARAM_NONCE);
+ OBX_TRACE_DEBUG2("nonce ind:%d, num:%d", ind, num_triplet);
+ if (ind == num_triplet || (p_triplet[ind].len < OBX_MIN_NONCE_SIZE) || (p_triplet[ind].len > OBX_NONCE_SIZE))
+ {
+ OBX_TRACE_ERROR0("No Nonce parameter");
+ return OBX_BAD_PARAMS;
+ }
+ p = data;
+ BTM_GetLocalDeviceAddr (p);
+
+ /* compute the session ID */
+ obx_session_id (p_sess_info, p_scb->peer_addr, p_triplet[ind].p_array, p_triplet[ind].len,
+ data, &p_scb->sess_info[OBX_SESSION_ID_SIZE], OBX_LOCAL_NONCE_SIZE);
+
+ return OBX_SUCCESS;
+}
+
+/*******************************************************************************
+** Function obx_sa_session_ind
+** Description process session request from client
+** when the session request is received is not OBX_SS_NOT_CONNECTED
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_session_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_TRIPLET triplet[OBX_MAX_SESS_PARAM_TRIP];
+ UINT8 num = OBX_MAX_SESS_PARAM_TRIP, ind;
+ UINT8 *p;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+ UINT8 rsp_code = OBX_RSP_FORBIDDEN;
+ BOOLEAN now = FALSE;
+#if (BT_USE_TRACES == TRUE)
+ tOBX_SESS_ST old_sess_st = p_scb->sess_st;
+#endif
+ tOBX_EVENT old_api_evt;
+ UINT32 obj_offset = 0;
+ UINT8 ind_to;
+ UINT32 timeout = OBX_INFINITE_TIMEOUT;
+ tOBX_SR_STATE state = OBX_SS_NULL;
+
+ OBX_TRACE_DEBUG0("obx_sa_session_ind");
+ OBX_ReadTriplet(p_pkt, OBX_HI_SESSION_PARAM, triplet, &num);
+ if (p_cb->nonce == 0)
+ {
+ OBX_TRACE_ERROR0("reliable session is not supported by this server");
+ /* do not report the session_req_evt */
+ p_scb->api_evt = OBX_NULL_EVT;
+ obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, OBX_RSP_NOT_IMPLEMENTED, NULL);
+ return OBX_SS_NULL;
+ }
+ else if (num)
+ {
+ ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP);
+ OBX_TRACE_DEBUG2("sess_op ind:%d, num:%d", ind, num);
+ if ((ind != num) && (triplet[ind].len == OBX_LEN_SESS_PARAM_SESS_OP))
+ {
+ p = triplet[ind].p_array;
+ p_scb->param.sess.sess_op = *p;
+ OBX_TRACE_DEBUG1("sess_op :%d", *p);
+ switch (*p)
+ {
+ case OBX_SESS_OP_CREATE:
+ /* do not report the API event */
+ p_scb->api_evt = OBX_NULL_EVT;
+ /* the session is already active; reject with Service Unavailable */
+ rsp_code = OBX_RSP_SERVICE_UNAVL;
+ break;
+
+ case OBX_SESS_OP_CLOSE:
+ /* verify that the session ID matches an existing one. Otherwise, FORBIDDEN */
+ if (p_scb->sess_st != OBX_SESS_NONE)
+ {
+ ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_ID);
+ if (ind == num || triplet[ind].len != OBX_SESSION_ID_SIZE)
+ break;
+ if (memcmp (p_scb->sess_info, triplet[ind].p_array, OBX_SESSION_ID_SIZE) != 0)
+ {
+ /* bad session id */
+ break;
+ }
+ /* must be closing a good session 0 send the response now */
+ now = TRUE;
+ rsp_code = OBX_RSP_OK;
+ p_scb->sess_st = OBX_SESS_CLOSE;
+ }
+ break;
+
+ case OBX_SESS_OP_SUSPEND:
+ /* verify that a session is active. Otherwise, FORBIDDEN */
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ rsp_code = OBX_RSP_OK;
+ p_scb->sess_st = OBX_SESS_SUSPEND;
+ p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_scb->prev_state;
+ p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_scb->srm;
+ /* save the session state in a GKI buffer on OBX_SessionRsp */
+ if (p_scb->prev_state == OBX_SS_PUT_INDICATED || p_scb->prev_state == OBX_SS_GET_INDICATED)
+ {
+ /* out of sequence suspend:
+ * report the suspend event when the PutRsp or GetRsp is called
+ * this would allow server to resume the session in the right state */
+ p_scb->api_evt = OBX_NULL_EVT;
+ p_scb->sess_st = OBX_SESS_SUSPENDING;
+ state = p_scb->prev_state;
+ }
+ }
+ break;
+
+ case OBX_SESS_OP_RESUME:
+ rsp_code = OBX_RSP_SERVICE_UNAVL;
+ /* do not report the API event */
+ p_scb->api_evt = OBX_NULL_EVT;
+ break;
+
+ case OBX_SESS_OP_SET_TIME:
+ /* respond SET_TIME right away */
+ now = TRUE;
+
+ if (p_scb->sess_st == OBX_SESS_NONE)
+ {
+ rsp_code = OBX_RSP_FORBIDDEN;
+ }
+ break;
+ }
+ }
+ }
+ OBX_TRACE_DEBUG6("obx_sa_session_ind tx_mtu: %d, sess_st:%d->%d, rsp_code:0x%x, now:%d pstate:%d",
+ p_scb->ll_cb.comm.tx_mtu, old_sess_st, p_scb->sess_st, rsp_code, now, p_scb->prev_state);
+
+ if (rsp_code == OBX_RSP_OK)
+ {
+ obx_read_timeout (triplet, num, &timeout, &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]);
+ }
+
+ if ((rsp_code != OBX_RSP_OK) || now)
+ {
+ /* hold the original api_evt temporarily, so it's not reported at this obx_ssm_event */
+ old_api_evt = p_scb->api_evt;
+ p_scb->api_evt = OBX_NULL_EVT;
+ /* send the response now */
+ obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, rsp_code, NULL);
+ /* restore the api event */
+ p_scb->api_evt = old_api_evt;
+ }
+ else
+ {
+ ind_to = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP);
+ if ((ind_to != num) && (triplet[ind_to].len == OBX_TIMEOUT_SIZE))
+ {
+ p = triplet[ind_to].p_array;
+ BE_STREAM_TO_UINT32(timeout, p);
+ }
+ p_scb->param.sess.p_sess_info = p_scb->sess_info;
+ p_scb->param.sess.sess_st = p_scb->sess_st;
+ p_scb->param.sess.ssn = p_scb->ssn;
+ p_scb->param.sess.obj_offset = obj_offset;
+ p_scb->param.sess.timeout = timeout;
+ memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN);
+
+ }
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_sess_conn_ind
+** Description process session request from client
+** when the session request is received in OBX_SS_NOT_CONNECTED
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_sess_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ UINT8 sess_info[OBX_SESSION_INFO_SIZE]; /* session id + local nonce */
+ tOBX_TRIPLET triplet[OBX_MAX_SESS_PARAM_TRIP];
+ UINT8 num = OBX_MAX_SESS_PARAM_TRIP, ind;
+ UINT8 *p;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+ UINT8 rsp_code = OBX_RSP_FORBIDDEN;
+ BOOLEAN now = FALSE;
+#if (BT_USE_TRACES == TRUE)
+ tOBX_SESS_ST old_sess_st = p_scb->sess_st;
+#endif
+ tOBX_SESS_OP sess_op = OBX_SESS_OP_CREATE;
+ tOBX_EVENT old_api_evt;
+ UINT32 obj_offset = 0;/* to report in evt param */
+ UINT8 op_ssn;
+ UINT8 num_trip = 0;
+ BT_HDR *p_rsp = NULL;
+ UINT8 data[12];
+ UINT8 ind_to;
+ UINT32 timeout = OBX_INFINITE_TIMEOUT;
+ UINT32 offset = 0; /* if non-0, add to triplet on resume */
+ tOBX_SPND_CB *p_spndcb;
+ tOBX_EVT_PARAM param; /* The event parameter. */
+
+ OBX_TRACE_DEBUG0("obx_sa_sess_conn_ind");
+ OBX_ReadTriplet(p_pkt, OBX_HI_SESSION_PARAM, triplet, &num);
+ if (p_cb->nonce == 0)
+ {
+ OBX_TRACE_ERROR0("reliable session is not supported by this server");
+ /* do not report the session_req_evt */
+ p_scb->api_evt = OBX_NULL_EVT;
+ obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, OBX_RSP_NOT_IMPLEMENTED, NULL);
+ return OBX_SS_NULL;
+ }
+ else if (num)
+ {
+ ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP);
+ OBX_TRACE_DEBUG2("sess_op ind:%d, num:%d", ind, num);
+ if ((ind != num) && (triplet[ind].len == OBX_LEN_SESS_PARAM_SESS_OP))
+ {
+ p = triplet[ind].p_array;
+ p_scb->param.sess.sess_op = sess_op = *p;
+ OBX_TRACE_DEBUG1("sess_op :%d", *p);
+ switch (*p)
+ {
+ case OBX_SESS_OP_CREATE:
+ /* do not report the API event */
+ p_scb->api_evt = OBX_NULL_EVT;
+
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ /* the session is already active; reject with OBX_RSP_FORBIDDEN */
+ break;
+ }
+
+ /* check if we still have room for one more session */
+ if (obx_find_suspended_session (p_scb, NULL, 0) == NULL)
+ {
+ rsp_code = OBX_RSP_DATABASE_FULL;
+ break;
+ }
+
+ p = &p_scb->sess_info[OBX_SESSION_INFO_NONCE_IDX];
+ UINT32_TO_BE_STREAM(p, p_cb->nonce);
+ p_cb->nonce++;
+ /* make sure it's not 0 (which means reliable session is disabled) */
+ if (!p_cb->nonce)
+ p_cb->nonce++;
+
+ if (obx_sa_make_sess_id (p_scb, p_scb->sess_info, triplet, num) == OBX_SUCCESS)
+ {
+ rsp_code = OBX_RSP_OK;
+ p_scb->ssn = 0;
+ p_scb->sess_st = OBX_SESS_CREATE;
+ }
+ break;
+
+ case OBX_SESS_OP_RESUME:
+ /* verify that a previously interrupted session exists with the same session parameters.
+ Otherwise, OBX_RSP_SERVICE_UNAVL.
+ */
+ p_spndcb = obx_find_suspended_session (p_scb, triplet, num);
+ if (p_spndcb)
+ {
+ op_ssn = p_spndcb->ssn;
+ memcpy (p_scb->sess_info, p_spndcb->sess_info, OBX_SESSION_INFO_SIZE);
+ if (obx_sa_make_sess_id (p_scb, sess_info, triplet, num) == OBX_SUCCESS &&
+ memcmp (sess_info, p_scb->sess_info, OBX_SESSION_ID_SIZE) == 0)
+ {
+ /* clear the suspend cb info */
+ p_spndcb->state = OBX_SS_NULL;
+ if (p_spndcb->stle.param)
+ {
+ btu_stop_timer (&p_spndcb->stle);
+ p_spndcb->stle.param = 0;
+ }
+ rsp_code = OBX_RSP_OK;
+ p_scb->sess_st = OBX_SESS_RESUME;
+ p_scb->srmp |= OBX_SRMP_SESS_FST;
+ ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_NSEQNUM);
+ if (ind != num)
+ {
+ /* ssn exists - must be immediate suspend */
+ p = triplet[ind].p_array;
+ op_ssn = *p;
+ obj_offset = obx_read_obj_offset(triplet, num);
+ }
+ }
+ obxu_dump_hex (p_scb->sess_info, "sess info", OBX_SESSION_INFO_SIZE);
+ OBX_TRACE_DEBUG4("p_spndcb->offset: 0x%x srm:x%x op_ssn %d ssn %d", p_spndcb->offset, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX],op_ssn,p_scb->ssn);
+
+ if (p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] & OBX_SRM_ENGAGE)
+ {
+ /*
+ If offset in the request is smaller which only happens when it is a get operation, then client's offset and ssn matters.
+ So server's offset and ssn should be set to the client's ones.
+ If offset in the request is greater which only happens when it is a put operation, then server's offset and ssn matters.
+ So server keeps its offset(do nothing) and ssn and send it back to client in the resume reponse.
+ If offset are equal, either side's offset & ssn is fine, we choose to use the one in the reqeust
+ */
+
+ offset = p_spndcb->offset;
+ if (obj_offset && (obj_offset <= offset))
+ {
+ offset = obj_offset;
+ p_scb->ssn = op_ssn;
+ /* Adjust ssn in the next continue to be the same as nssn in the resume request */
+ if (p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] ==OBX_CS_GET_SRM)
+ p_scb->ssn--;
+
+
+ }
+
+ }
+ /* SRM is not enabled */
+ else
+ {
+ p_scb->ssn = op_ssn;
+ }
+
+ OBX_TRACE_DEBUG4("offset: 0x%x ssn:%d obj_offset:0x%x srm:0x%x", offset, p_scb->ssn, obj_offset, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX]);
+ }
+
+
+ if (rsp_code != OBX_RSP_OK)
+ {
+ rsp_code = OBX_RSP_SERVICE_UNAVL;
+ }
+ /* do not report the API event for RESUME.
+ * if OBX_RSP_OK, the event is reported in this function.
+ * p_rsp is used to call obx_ssm_event, and can not be reported to cback */
+ p_scb->api_evt = OBX_NULL_EVT;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* do not have session parameters - bad req do not report the API event */
+ p_scb->api_evt = OBX_NULL_EVT;
+ rsp_code = OBX_RSP_BAD_REQUEST;
+ }
+ OBX_TRACE_DEBUG5("obx_sa_sess_conn_ind tx_mtu: %d, sess_st:%d->%d, rsp_code:0x%x, now:%d",
+ p_scb->ll_cb.comm.tx_mtu, old_sess_st, p_scb->sess_st, rsp_code, now);
+
+ if (rsp_code == OBX_RSP_OK)
+ {
+ /* send the session response now.
+ * do not report OBX_SESSION_REQ_EVT until connect indication,
+ * so connection id can be reported at the same time in sess_info */
+ if ( (p_rsp = OBX_HdrInit(p_scb->handle, OBX_MIN_MTU))== NULL)
+ {
+ rsp_code = OBX_RSP_INTRNL_SRVR_ERR;
+ }
+ else
+ {
+ obx_read_timeout (triplet, num, &timeout, &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]);
+
+ p = (UINT8 *) (p_rsp + 1) + p_rsp->offset;
+ /* response packet always has the final bit set */
+ *p++ = (OBX_RSP_OK | OBX_FINAL);
+ p_rsp->len = 3;
+ p = data;
+
+ /* add address */
+ triplet[num_trip].tag = OBX_TAG_SESS_PARAM_ADDR;
+ triplet[num_trip].len = BD_ADDR_LEN;
+ triplet[num_trip].p_array = p;
+ BTM_GetLocalDeviceAddr (p);
+ p += BD_ADDR_LEN;
+ num_trip++;
+
+ /* add nonce 4 - 16 bytes */
+ triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NONCE;
+ triplet[num_trip].len = OBX_LOCAL_NONCE_SIZE;
+ triplet[num_trip].p_array = &p_scb->sess_info[OBX_SESSION_INFO_NONCE_IDX];
+ num_trip++;
+
+ /* add session id */
+ triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_ID;
+ triplet[num_trip].len = OBX_SESSION_ID_SIZE;
+ triplet[num_trip].p_array = p_scb->sess_info;
+ num_trip++;
+
+ if (sess_op == OBX_SESS_OP_RESUME)
+ {
+ /* add session id */
+ triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NSEQNUM;
+ triplet[num_trip].len = 1;
+ triplet[num_trip].p_array = p;
+ /* Adjust ssn in the resume response to be the same as nssn in the resume request */
+ if (p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] == OBX_CS_GET_SRM)
+ *p++ = p_scb->ssn+1;
+ else
+ *p++ = p_scb->ssn;
+ num_trip++;
+ if (offset)
+ {
+ triplet[num_trip].tag = OBX_TAG_SESS_PARAM_OBJ_OFF;
+ triplet[num_trip].len = OBX_LEN_SESS_PARAM_OBJ_OFF;
+ triplet[num_trip].p_array = p;
+ UINT32_TO_BE_STREAM(p, offset);
+ num_trip++;
+ obj_offset = offset;
+ }
+ }
+
+ /* add timeout */
+ if (timeout != OBX_INFINITE_TIMEOUT && obx_cb.sess_tout_val != OBX_INFINITE_TIMEOUT && (obx_cb.sess_tout_val > timeout))
+ {
+ timeout = obx_cb.sess_tout_val;
+ triplet[num_trip].p_array = p;
+ num_trip += obx_add_timeout (&triplet[num_trip], obx_cb.sess_tout_val, &p_scb->param.sess);
+ p = &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX];
+ UINT32_TO_BE_STREAM(p, timeout);
+ }
+ OBX_AddTriplet(p_rsp, OBX_HI_SESSION_PARAM, triplet, num_trip);
+
+ /* adjust the packet len */
+ p = (UINT8 *) (p_rsp + 1) + p_rsp->offset + 1;
+ UINT16_TO_BE_STREAM(p, p_rsp->len);
+ p_rsp->event = OBX_SESSION_CFM_SEVT + 1;
+ p_scb->sess_st = OBX_SESS_ACTIVE;
+ p_scb->param.sess.p_sess_info = p_scb->sess_info;
+ p_scb->param.sess.sess_st = p_scb->sess_st;
+ p_scb->param.sess.ssn = p_scb->ssn;
+ p_scb->param.sess.nssn = p_scb->ssn;
+ p_scb->param.sess.obj_offset = obj_offset;
+ p_scb->param.sess.timeout = timeout;
+ memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN);
+ obx_ssm_event(p_scb, OBX_SESSION_CFM_SEVT, p_rsp);
+
+ if (sess_op == OBX_SESS_OP_RESUME)
+ {
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_REQ_EVT, p_scb->param, NULL);
+ memset(&p_scb->param, 0, sizeof (p_scb->param) );
+ param.conn.ssn = p_scb->ssn;
+ memcpy (param.conn.peer_addr, p_scb->peer_addr, BD_ADDR_LEN);
+ p = &p_scb->sess_info[OBX_SESSION_INFO_MTU_IDX];
+ BE_STREAM_TO_UINT16(param.conn.mtu, p);
+ p_scb->ll_cb.comm.tx_mtu = param.conn.mtu;
+ param.conn.handle = p_scb->ll_cb.comm.handle;
+ OBX_TRACE_DEBUG1("RESUME tx_mtu: %d", p_scb->ll_cb.comm.tx_mtu);
+ /* report OBX_CONNECT_REQ_EVT to let the client know the MTU */
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CONNECT_REQ_EVT, param, NULL);
+ }
+ }
+ }
+
+ if ((rsp_code != OBX_RSP_OK) || now)
+ {
+ /* hold the original api_evt temporarily, so it's not reported at this obx_ssm_event */
+ old_api_evt = p_scb->api_evt;
+ p_scb->api_evt = OBX_NULL_EVT;
+ /* send the response now */
+ obx_prepend_rsp_msg(p_scb->handle, OBX_DISCNT_CFM_SEVT, rsp_code, NULL);
+ /* restore the api event */
+ p_scb->api_evt = old_api_evt;
+ }
+ else
+ {
+ ind_to = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP);
+ if ((ind_to != num) && (triplet[ind_to].len == OBX_TIMEOUT_SIZE))
+ {
+ p = triplet[ind_to].p_array;
+ BE_STREAM_TO_UINT32(timeout, p);
+ }
+ p_scb->param.sess.p_sess_info = p_scb->sess_info;
+ p_scb->param.sess.sess_st = p_scb->sess_st;
+ p_scb->param.sess.ssn = p_scb->ssn;
+ p_scb->param.sess.nssn = p_scb->ssn;
+ p_scb->param.sess.obj_offset = obj_offset;
+ p_scb->param.sess.timeout = timeout;
+ memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN);
+
+ }
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_wc_sess_ind
+** Description process session request from client
+** when the session request is received in OBX_SS_WAIT_CLOSE
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_wc_sess_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_EVT_PARAM param;
+ tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle);
+
+ OBX_TRACE_DEBUG1("obx_sa_wc_sess_ind sess_st:%d", p_scb->sess_st);
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ /* processing CloseSession */
+ obx_sa_session_ind(p_scb, p_pkt);
+ }
+ else
+ {
+ /* probably CreateSession */
+ obx_sa_sess_conn_ind(p_scb, p_pkt);
+ OBX_TRACE_DEBUG1("obx_sa_wc_sess_ind (after obx_sa_sess_conn_ind) sess_st:%d", p_scb->sess_st);
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ /* after session command and still is Active
+ * a session must have been created during Wait_close state.
+ * need to report a OBX_CLOSE_IND_EVT to clean up the profiles */
+ memset(&param, 0, sizeof(tOBX_EVT_PARAM));
+ if (p_cb && p_cb->p_cback)
+ (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, NULL);
+ }
+ }
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_session_rsp
+** Description process Session response API.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_session_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE new_state = OBX_SS_NULL;
+ UINT8 *p;
+ tOBX_L2C_CB *p_lcb;
+ tOBX_L2C_EVT_PARAM evt_param;
+
+ OBX_TRACE_DEBUG1("obx_sa_session_rsp pstate:%d", p_scb->prev_state);
+ new_state = obx_sa_snd_rsp(p_scb, p_pkt);
+ OBX_TRACE_DEBUG2("sess_st: %d op:%d", p_scb->sess_st, p_scb->param.sess.sess_op);
+ if (p_scb->sess_st == OBX_SESS_ACTIVE && p_scb->param.sess.sess_op == OBX_SESS_OP_RESUME)
+ {
+ p_scb->srm = p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX];
+ new_state = p_scb->sess_info[OBX_SESSION_INFO_ST_IDX];
+ p = &p_scb->sess_info[OBX_SESSION_INFO_ID_IDX];
+ BE_STREAM_TO_UINT32(p_scb->conn_id, p);
+ OBX_TRACE_DEBUG3("new_state; %d Connection ID: 0x%x, srm:0x%x", new_state, p_scb->conn_id, p_scb->srm);
+ if ((p_scb->srm & OBX_SRM_ENGAGE) && (new_state == OBX_SS_GET_SRM))
+ {
+ p_lcb = &p_scb->ll_cb.l2c;
+ evt_param.any = 0;
+ obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_RESUME);
+ }
+ /* report OBX_CONNECT_REQ_EVT in obx_sa_sess_conn_ind()
+ if (new_state == OBX_SS_CONNECTED)
+ {
+ } */
+ }
+ else if (p_scb->sess_st == OBX_SESS_CLOSE)
+ {
+ new_state = OBX_SS_WAIT_CLOSE;
+ }
+
+ return new_state;
+}
+
+/*******************************************************************************
+** Function obx_sa_put_ind
+** Description received a PUT request from the client. Check if SRM is engaged.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_put_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ if ((p_scb->srm & OBX_SRM_REQING) && (p_scb->srm & OBX_SRM_ENABLE))
+ {
+ state = OBX_CS_PUT_SRM;
+ }
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_srm_put_req
+** Description received a PUT request from the client when SRM is engaged.
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_srm_put_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ if (!p_scb->param.put.final)
+ p_scb->srm |= OBX_SRM_WAIT_UL;
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_srm_put_rsp
+** Description process PUT response API function.
+** report PUT request event, if any is queued
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_srm_put_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ tOBX_COMM_CB *p_comm = &p_scb->ll_cb.comm;
+ UINT8 rsp_code = OBX_RSP_DEFAULT;
+ BOOLEAN ret = TRUE;
+
+ obx_access_rsp_code(p_pkt, &rsp_code);
+ rsp_code &= ~OBX_FINAL;
+ if (rsp_code != OBX_RSP_CONTINUE )
+ {
+ p_scb->srm |= OBX_SRM_NEXT;
+ if (rsp_code != OBX_RSP_OK)
+ p_scb->srm |= OBX_SRM_ABORT;
+ }
+
+ p_scb->srm &= ~OBX_SRM_WAIT_UL;
+ OBX_TRACE_DEBUG2("obx_sa_srm_put_rsp srm:0x%x rsp_code:0x%x", p_scb->srm, rsp_code);
+ if (p_scb->srm & OBX_SRM_NEXT)
+ {
+ p_scb->srm &= ~OBX_SRM_NEXT;
+ state = obx_sa_snd_rsp (p_scb, p_pkt);
+ }
+ else
+ {
+ if (p_scb->sess_st == OBX_SESS_ACTIVE)
+ {
+ p_scb->ssn++;
+ }
+ obx_start_timer(&p_scb->ll_cb.comm);
+ if (p_pkt)
+ GKI_freebuf(p_pkt);
+ }
+ OBX_TRACE_DEBUG1("obx_sa_srm_put_rsp srm:0x%x", p_scb->srm);
+
+ while (ret && (p_pkt = (BT_HDR *)GKI_dequeue (&p_comm->rx_q)) != NULL)
+ {
+ if (state != OBX_SS_NULL)
+ {
+ p_scb->state = state;
+ state = OBX_SS_NULL;
+ }
+ ret = obx_sr_proc_pkt (p_scb, p_pkt);
+ if ((p_scb->srm & OBX_SRM_ABORT) == 0)
+ ret = FALSE;
+ obx_flow_control(p_comm);
+ OBX_TRACE_DEBUG3("obx_sa_srm_put_rsp rx_q.count: %d srm:0x%x, ret:%d", p_comm->rx_q.count, p_scb->srm, ret );
+ }
+
+ return state;
+}
+
+/*******************************************************************************
+** Function obx_sa_srm_get_fcs
+** Description Process L2CAP congestion event
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_srm_get_fcs(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ BOOLEAN not_cong = TRUE;
+ if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg)
+ {
+ OBX_TRACE_DEBUG1("obx_sa_srm_get_fcs cong:%d", p_scb->ll_cb.l2c.cong);
+ if (p_scb->ll_cb.l2c.cong)
+ not_cong = FALSE;
+ }
+ if (not_cong)
+ p_scb->api_evt = OBX_GET_REQ_EVT;
+ return OBX_SS_NULL;
+}
+
+/*******************************************************************************
+** Function obx_sa_srm_get_rsp
+** Description send GET response to client
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_srm_get_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state;
+ OBX_TRACE_DEBUG1("obx_sa_srm_get_rsp srm:0x%x", p_scb->srm);
+ state = obx_sa_snd_rsp(p_scb, p_pkt);
+ return state;
+}
+
+
+
+/*******************************************************************************
+** Function obx_sa_srm_get_req
+** Description process GET request from client
+*******************************************************************************/
+tOBX_SR_STATE obx_sa_srm_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt)
+{
+ tOBX_SR_STATE state = OBX_SS_NULL;
+ tOBX_SR_CB *p_cb;
+
+ OBX_TRACE_DEBUG3("obx_sa_srm_get_req srm:0x%x srmp:0x%x final:%d", p_scb->srm, p_scb->srmp, p_scb->param.get.final);
+ if (p_scb->srmp & OBX_SRMP_NONF)
+ {
+ /* the GET request does not set the final bit yet.
+ * merge this request, send response automatically and do not report the event */
+ if (!p_scb->param.get.final)
+ {
+ p_scb->p_saved_msg = obx_merge_get_req(p_scb->p_saved_msg, p_pkt);
+ p_scb->api_evt = OBX_NULL_EVT;
+ }
+ else
+ {
+ p_scb->srmp &= ~OBX_SRMP_NONF;
+ p_pkt = obx_merge_get_req(p_scb->p_saved_msg, p_pkt);
+ p_scb->p_saved_msg = NULL;
+ p_scb->api_evt = OBX_NULL_EVT;
+ p_cb = &obx_cb.server[p_scb->handle - 1];
+ (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, p_pkt);
+ memset(&p_scb->param, 0, sizeof (p_scb->param) );
+ }
+ }
+
+ return state;
+}
+