summaryrefslogtreecommitdiff
path: root/lib/bt/host/bluedroid/stack/goep
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2025-07-25 13:33:07 +1000
committerjacqueline <me@jacqueline.id.au>2025-07-25 13:33:07 +1000
commitc8e79a926620e48830778714cfe4b2ea2453fcaf (patch)
tree8c756e08e01b8e147cf72bec128026f46bd854c5 /lib/bt/host/bluedroid/stack/goep
parent237136f3e93cb6b5be24670d7520adb17cc0fa36 (diff)
downloadtangara-fw-c8e79a926620e48830778714cfe4b2ea2453fcaf.tar.gz
Update forked idf components
Diffstat (limited to 'lib/bt/host/bluedroid/stack/goep')
-rw-r--r--lib/bt/host/bluedroid/stack/goep/goepc_api.c376
-rw-r--r--lib/bt/host/bluedroid/stack/goep/goepc_main.c528
-rw-r--r--lib/bt/host/bluedroid/stack/goep/include/goep_int.h103
3 files changed, 1007 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/goep/goepc_api.c b/lib/bt/host/bluedroid/stack/goep/goepc_api.c
new file mode 100644
index 00000000..06be8f59
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/goep/goepc_api.c
@@ -0,0 +1,376 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "osi/osi.h"
+#include "osi/allocator.h"
+#include "common/bt_target.h"
+
+#include "stack/obex_api.h"
+#include "stack/goep_common.h"
+#include "stack/goepc_api.h"
+#include "goep_int.h"
+
+#if (GOEPC_INCLUDED == TRUE)
+
+/*******************************************************************************
+**
+** Function GOEPC_Init
+**
+** Description Initialize GOEP Client role, must call before using any
+** other GOEPC APIs
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_Init(void)
+{
+#if (GOEP_DYNAMIC_MEMORY)
+ if (!goepc_cb_ptr) {
+ goepc_cb_ptr = (tGOEPC_CB *)osi_malloc(sizeof(tGOEPC_CB));
+ if (!goepc_cb_ptr) {
+ return GOEP_NO_RESOURCES;
+ }
+ }
+#endif /* #if (GOEP_DYNAMIC_MEMORY) */
+ memset(&goepc_cb, 0, sizeof(tGOEPC_CB));
+ goepc_cb.trace_level = BT_TRACE_LEVEL_ERROR;
+ return GOEP_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_Deinit
+**
+** Description Deinit GOEP Client role, once deinit, can not use any other
+** GOEPC APIs until call GOEPC_Init again
+**
+*******************************************************************************/
+void GOEPC_Deinit(void)
+{
+#if (GOEP_DYNAMIC_MEMORY)
+ if (goepc_cb_ptr) {
+ osi_free(goepc_cb_ptr);
+ goepc_cb_ptr = NULL;
+ }
+#endif /* #if (GOEP_DYNAMIC_MEMORY) */
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_Open
+**
+** Description Start the progress to establish a GOEP connection to server
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed, when the
+** connection is established, GOEPC_OPENED_EVT will come
+**
+*******************************************************************************/
+UINT16 GOEPC_Open(tOBEX_SVR_INFO *svr, tGOEPC_EVT_CBACK callback, UINT16 *out_handle)
+{
+ UINT16 ret = GOEP_SUCCESS;
+ tGOEPC_CCB *p_ccb = NULL;
+
+ do {
+ /* check parameter, allow out_handle to be NULL */
+ if (svr == NULL || callback == NULL) {
+ ret = GOEP_INVALID_PARAM;
+ break;
+ }
+
+ p_ccb = goepc_allocate_ccb();
+ if (p_ccb == NULL) {
+ ret = GOEP_NO_RESOURCES;
+ break;
+ }
+
+ if (OBEX_CreateConn(svr, goepc_obex_callback, &p_ccb->obex_handle) != OBEX_SUCCESS) {
+ ret = GOEP_TL_ERROR;
+ break;
+ }
+
+ /* success */
+ p_ccb->callback = callback;
+ p_ccb->state = GOEPC_STATE_OPENING;
+ if (out_handle) {
+ *out_handle = p_ccb->allocated;
+ }
+ } while (0);
+
+ if (ret != GOEP_SUCCESS && p_ccb != NULL) {
+ goepc_free_ccb(p_ccb);
+ }
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_Close
+**
+** Description Close a GOEP connection immediately
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_Close(UINT16 handle)
+{
+ tGOEPC_CCB *p_ccb = NULL;
+
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ return GOEP_BAD_HANDLE;
+ }
+
+ p_ccb = &goepc_cb.ccb[ccb_idx];
+ if (p_ccb->obex_handle) {
+ OBEX_RemoveConn(p_ccb->obex_handle);
+ }
+ goepc_free_ccb(p_ccb);
+
+ return GOEP_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_SendRequest
+**
+** Description Send the prepared request packet to server
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_SendRequest(UINT16 handle)
+{
+ UINT16 ret = GOEP_SUCCESS;
+ tGOEPC_CCB *p_ccb = NULL;
+ BOOLEAN final = FALSE;
+
+ do {
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ ret = GOEP_BAD_HANDLE;
+ break;
+ }
+ p_ccb = &goepc_cb.ccb[ccb_idx];
+
+ if (p_ccb->pkt == NULL) {
+ ret = GOEP_INVALID_STATE;
+ break;
+ }
+
+ final = OBEX_CheckFinalBit(p_ccb->pkt);
+ /* check whether state machine allow this operation */
+ if (!goepc_check_obex_req_allow(p_ccb->state, final)) {
+ ret = GOEP_INVALID_STATE;
+ break;
+ }
+
+ if (p_ccb->congest) {
+ ret = GOEP_CONGEST;
+ break;
+ }
+
+ /* execute srm state machine */
+ goepc_srm_sm_execute(p_ccb, TRUE, p_ccb->pkt_srm_en, p_ccb->pkt_srm_wait);
+
+ tGOEPC_DATA data;
+ data.pkt = p_ccb->pkt;
+
+ p_ccb->last_pkt_opcode = p_ccb->curr_pkt_opcode;
+ p_ccb->pkt = NULL;
+ p_ccb->pkt_srm_en = FALSE;
+ p_ccb->pkt_srm_wait = FALSE;
+
+ /* execute main state machine */
+ if (final) {
+ goepc_sm_execute(p_ccb, GOEPC_SM_EVENT_REQ_FB, &data);
+ }
+ else {
+ goepc_sm_execute(p_ccb, GOEPC_SM_EVENT_REQ, &data);
+ }
+ /* since goepc_sm_execute may free ccb, can not access ccb here */
+ } while (0);
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_PrepareRequest
+**
+** Description Prepare a request packet, packet will be store internally
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_PrepareRequest(UINT16 handle, tOBEX_PARSE_INFO *info, UINT16 buff_size)
+{
+ UINT16 ret = GOEP_SUCCESS;
+ tGOEPC_CCB *p_ccb = NULL;
+ BT_HDR *pkt = NULL;
+
+ do {
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ ret = GOEP_BAD_HANDLE;
+ break;
+ }
+ p_ccb = &goepc_cb.ccb[ccb_idx];
+
+ if (info == NULL || buff_size < OBEX_MIN_PACKET_SIZE) {
+ ret = GOEP_INVALID_PARAM;
+ break;
+ }
+
+ if (p_ccb->pkt != NULL) {
+ ret = GOEP_INVALID_STATE;
+ break;
+ }
+
+ if (!goepc_check_obex_req_param(info)) {
+ ret = GOEP_INVALID_PARAM;
+ break;
+ }
+
+ if (OBEX_BuildRequest(info, buff_size, &pkt) != OBEX_SUCCESS) {
+ ret = GOEP_NO_RESOURCES;
+ break;
+ }
+
+ p_ccb->curr_pkt_opcode = info->opcode;
+ p_ccb->pkt = pkt;
+ } while (0);
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_DropRequest
+**
+** Description Drop the prepared internal request packet
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_DropRequest(UINT16 handle)
+{
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ return GOEP_BAD_HANDLE;
+ }
+
+ tGOEPC_CCB *p_ccb = &goepc_cb.ccb[ccb_idx];
+ if (p_ccb->pkt == NULL) {
+ return GOEP_INVALID_STATE;
+ }
+
+ osi_free(p_ccb->pkt);
+ p_ccb->pkt = NULL;
+ p_ccb->pkt_srm_en = FALSE;
+ p_ccb->pkt_srm_wait = FALSE;
+ return GOEP_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_RequestSetSRM
+**
+** Description Modify the prepared internal request packet, append SRM header
+** or SRMP header
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_RequestSetSRM(UINT16 handle, BOOLEAN srm_en, BOOLEAN srm_wait)
+{
+ UINT16 ret = GOEP_SUCCESS;
+ tGOEPC_CCB *p_ccb = NULL;
+
+ do {
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ ret = GOEP_BAD_HANDLE;
+ break;
+ }
+ p_ccb = &goepc_cb.ccb[ccb_idx];
+
+ if (!srm_en && !srm_wait) {
+ ret = GOEP_INVALID_PARAM;
+ break;
+ }
+
+ if (p_ccb->pkt == NULL) {
+ ret = GOEP_INVALID_STATE;
+ break;
+ }
+
+ if (srm_en) {
+ if (OBEX_AppendHeaderSRM(p_ccb->pkt, OBEX_SRM_ENABLE) == OBEX_SUCCESS) {
+ p_ccb->pkt_srm_en = TRUE;
+ }
+ else {
+ ret = GOEP_NO_RESOURCES;
+ break;
+ }
+ }
+ if (srm_wait) {
+ if (OBEX_AppendHeaderSRMP(p_ccb->pkt, OBEX_SRMP_WAIT) == OBEX_SUCCESS) {
+ p_ccb->pkt_srm_wait = TRUE;
+ }
+ else {
+ ret = GOEP_NO_RESOURCES;
+ break;
+ }
+ }
+ } while (0);
+
+ return ret;
+}
+
+/*******************************************************************************
+**
+** Function GOEPC_RequestAddHeader
+**
+** Description Modify the prepared internal request packet, append header
+**
+** Returns GOEP_SUCCESS if successful, otherwise failed
+**
+*******************************************************************************/
+UINT16 GOEPC_RequestAddHeader(UINT16 handle, UINT8 header_id, const UINT8 *data, UINT16 data_len)
+{
+ UINT16 ret = GOEP_SUCCESS;
+ tGOEPC_CCB *p_ccb = NULL;
+
+ do {
+ UINT16 ccb_idx = handle - 1;
+ if (ccb_idx >= GOEPC_MAX_CONNECTION || !goepc_cb.ccb[ccb_idx].allocated) {
+ ret = GOEP_BAD_HANDLE;
+ break;
+ }
+ p_ccb = &goepc_cb.ccb[ccb_idx];
+
+ if (p_ccb->pkt == NULL) {
+ ret = GOEP_INVALID_STATE;
+ break;
+ }
+
+ if ((data == NULL && data_len != 0) || (data != NULL && data_len == 0)) {
+ ret = GOEP_INVALID_PARAM;
+ break;
+ }
+
+ if (OBEX_AppendHeaderRaw(p_ccb->pkt, header_id, data, data_len) != OBEX_SUCCESS) {
+ ret = GOEP_NO_RESOURCES;
+ break;
+ }
+ } while (0);
+
+ return ret;
+}
+
+#endif /* #if (GOEPC_INCLUDED == TRUE) */
diff --git a/lib/bt/host/bluedroid/stack/goep/goepc_main.c b/lib/bt/host/bluedroid/stack/goep/goepc_main.c
new file mode 100644
index 00000000..e0859580
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/goep/goepc_main.c
@@ -0,0 +1,528 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <string.h>
+
+#include "osi/osi.h"
+#include "osi/allocator.h"
+#include "common/bt_target.h"
+
+#include "stack/obex_api.h"
+#include "stack/goep_common.h"
+#include "stack/goepc_api.h"
+#include "goep_int.h"
+
+#if (GOEPC_INCLUDED == TRUE)
+
+#if GOEP_DYNAMIC_MEMORY == FALSE
+tGOEPC_CB goepc_cb;
+#else
+tGOEPC_CB *goepc_cb_ptr = NULL;
+#endif
+
+tGOEPC_CCB *goepc_allocate_ccb(void)
+{
+ tGOEPC_CCB *p_ccb = NULL;
+ for (int i = 0; i < GOEPC_MAX_CONNECTION; ++i) {
+ if (!goepc_cb.ccb[i].allocated) {
+ goepc_cb.ccb[i].allocated = i + 1;
+ p_ccb = &goepc_cb.ccb[i];
+ break;
+ }
+ }
+ return p_ccb;
+}
+
+void goepc_free_ccb(tGOEPC_CCB *p_ccb)
+{
+ if (p_ccb->pkt != NULL) {
+ osi_free(p_ccb->pkt);
+ }
+ memset(p_ccb, 0, sizeof(tGOEPC_CCB));
+}
+
+BOOLEAN goepc_check_obex_req_param(tOBEX_PARSE_INFO *info)
+{
+ BOOLEAN ret = TRUE;
+ switch (info->opcode)
+ {
+ case OBEX_OPCODE_CONNECT:
+ if (info->max_packet_length < 255 || info->obex_version_number == 0) {
+ ret = FALSE;
+ }
+ break;
+ case OBEX_OPCODE_DISCONNECT:
+ case OBEX_OPCODE_PUT:
+ case OBEX_OPCODE_PUT_FINAL:
+ case OBEX_OPCODE_GET:
+ case OBEX_OPCODE_GET_FINAL:
+ case OBEX_OPCODE_SETPATH:
+ case OBEX_OPCODE_ACTION:
+ case OBEX_OPCODE_SESSION:
+ /* opcode allowed */
+ break;
+ case OBEX_OPCODE_ABORT:
+ default:
+ ret = FALSE;
+ /* opcode not allowed */
+ break;
+ }
+
+ return ret;
+}
+
+static tGOEPC_CCB *find_ccb_by_obex_handle(UINT16 obex_handle)
+{
+ tGOEPC_CCB *p_ccb = NULL;
+ for (int i = 0; i < GOEPC_MAX_CONNECTION; ++i) {
+ if (goepc_cb.ccb[i].allocated && goepc_cb.ccb[i].obex_handle == obex_handle) {
+ p_ccb = &goepc_cb.ccb[i];
+ }
+ }
+ return p_ccb;
+}
+
+static void goepc_extra_srm_rsp(UINT8 opcode, BT_HDR *pkt, BOOLEAN *srm_en, BOOLEAN *srm_wait)
+{
+ tOBEX_PARSE_INFO info;
+ BOOLEAN srm_found = FALSE;
+ BOOLEAN srmp_found = FALSE;
+ if (OBEX_ParseResponse(pkt, opcode, &info) == OBEX_SUCCESS) {
+ UINT8 *header = NULL;
+ while((header = OBEX_GetNextHeader(pkt, &info)) != NULL) {
+ switch (*header)
+ {
+ case OBEX_HEADER_ID_SRM:
+ if (header[1] == OBEX_SRM_ENABLE) {
+ *srm_en = TRUE;
+ }
+ srm_found = TRUE;
+ break;
+ case OBEX_HEADER_ID_SRM_PARAM:
+ switch (header[1])
+ {
+ case OBEX_SRMP_ADD_PKT:
+ /* goep should not use this */
+ break;
+ case OBEX_SRMP_WAIT:
+ *srm_wait = TRUE;
+ break;
+ case OBEX_SRMP_ADD_PKT_WAIT:
+ /* goep should not use this */
+ break;
+ default:
+ break;
+ }
+ srmp_found = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (srm_found && srmp_found) {
+ break;
+ }
+ }
+ }
+}
+
+static void goepc_act_congest(tGOEPC_CCB *p_ccb)
+{
+ p_ccb->congest = TRUE;
+ p_ccb->callback(p_ccb->allocated, GOEPC_CONGEST_EVT, NULL);
+}
+
+static void goepc_act_uncongest(tGOEPC_CCB *p_ccb)
+{
+ p_ccb->congest = FALSE;
+ p_ccb->callback(p_ccb->allocated, GOEPC_UNCONGEST_EVT, NULL);
+}
+
+static void goepc_act_mtu_chg(tGOEPC_CCB *p_ccb, tGOEPC_MTU_CHG *mtu_chg)
+{
+ tGOEPC_MSG msg;
+ msg.mtu_changed.peer_mtu = mtu_chg->peer_mtu;
+ msg.mtu_changed.our_mtu = mtu_chg->our_mtu;
+ p_ccb->peer_mtu = mtu_chg->peer_mtu;
+ p_ccb->our_mtu = mtu_chg->our_mtu;
+ p_ccb->callback(p_ccb->allocated, GOEPC_MTU_CHANGED_EVT, &msg);
+}
+
+void goepc_obex_callback(UINT16 handle, UINT8 event, tOBEX_MSG *msg)
+{
+ tGOEPC_DATA data;
+ UINT8 goepc_sm_event = GOEPC_SM_EVENT_DISCONNECT;
+ BOOLEAN exec_sm = FALSE;
+ tGOEPC_CCB *p_ccb = find_ccb_by_obex_handle(handle);
+ if (p_ccb == NULL) {
+ GOEPC_TRACE_ERROR("goepc_obex_callback can not find a ccb\n");
+ /* can not find a ccb in goepc, free resource and remove this connection */
+ if (event == OBEX_DATA_EVT && msg->data.pkt) {
+ osi_free(msg->data.pkt);
+ }
+ OBEX_RemoveConn(handle);
+ return;
+ }
+
+ switch (event)
+ {
+ case OBEX_CONNECT_EVT:
+ data.connected.peer_mtu = msg->connect.peer_mtu;
+ data.connected.our_mtu = msg->connect.our_mtu;
+ goepc_sm_event = GOEPC_SM_EVENT_CONNECT;
+ exec_sm = TRUE;
+ break;
+ case OBEX_MTU_CHANGE_EVT:
+ data.mtu_chg.peer_mtu = msg->mtu_change.peer_mtu;
+ data.mtu_chg.our_mtu = msg->mtu_change.our_mtu;
+ goepc_act_mtu_chg(p_ccb, &data.mtu_chg);
+ break;
+ case OBEX_DISCONNECT_EVT:
+ /* when we received this event, obex connection already disconnect */
+ p_ccb->obex_handle = 0;
+ goepc_sm_event = GOEPC_SM_EVENT_DISCONNECT;;
+ exec_sm = TRUE;
+ break;
+ case OBEX_CONGEST_EVT:
+ goepc_act_congest(p_ccb);
+ break;
+ case OBEX_UNCONGEST_EVT:
+ goepc_act_uncongest(p_ccb);
+ break;
+ case OBEX_DATA_EVT:
+ data.pkt = msg->data.pkt;
+ if (OBEX_CheckContinueResponse(data.pkt)) {
+ /* in OBEX 1.0, final bit of response code will always set, we need to check this */
+ goepc_sm_event = GOEPC_SM_EVENT_RSP;
+ }
+ else if (OBEX_CheckFinalBit(data.pkt)) {
+ goepc_sm_event = GOEPC_SM_EVENT_RSP_FB;
+ }
+ else {
+ goepc_sm_event = GOEPC_SM_EVENT_RSP;
+ }
+ exec_sm = TRUE;
+ break;
+ default:
+ /* other event, ignore */
+ break;
+ }
+
+ if (exec_sm) {
+ goepc_sm_execute(p_ccb, goepc_sm_event, &data);
+ }
+}
+
+static void goepc_sm_act_connect(tGOEPC_CCB *p_ccb, tGOEPC_CONNECTED *connected)
+{
+ tGOEPC_MSG msg;
+ msg.opened.peer_mtu = connected->peer_mtu;
+ msg.opened.our_mtu = connected->our_mtu;
+ p_ccb->peer_mtu = connected->peer_mtu;
+ p_ccb->our_mtu = connected->our_mtu;
+
+ /* main state machine transfer to OPENED_IDLE */
+ p_ccb->state = GOEPC_STATE_OPENED_IDLE;
+ p_ccb->callback(p_ccb->allocated, GOEPC_OPENED_EVT, &msg);
+}
+
+static void goepc_sm_act_disconnect(tGOEPC_CCB *p_ccb)
+{
+ tGOEPC_MSG msg;
+ if (p_ccb->obex_handle) {
+ OBEX_RemoveConn(p_ccb->obex_handle);
+ }
+ msg.closed.reason = GOEP_TL_ERROR;
+ p_ccb->callback(p_ccb->allocated, GOEPC_CLOSED_EVT, &msg);
+ /* free ccb, main state machine end */
+ goepc_free_ccb(p_ccb);
+}
+
+static void goepc_sm_act_send_req(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
+{
+ UINT16 ret = OBEX_SendPacket(p_ccb->obex_handle, pkt);
+ if (ret == OBEX_SUCCESS) {
+ /* main state machine transfer to OPENED_REQ */
+ p_ccb->state = GOEPC_STATE_OPENED_REQ;
+ }
+ else {
+ /* send failed, something error in transport layer, disconnect */
+ goepc_sm_act_disconnect(p_ccb);
+ }
+}
+
+static void goepc_sm_act_send_req_fb(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
+{
+ UINT16 ret = OBEX_SendPacket(p_ccb->obex_handle, pkt);
+ if (ret == OBEX_SUCCESS) {
+ /* main state machine transfer to OPENED_RSP */
+ p_ccb->state = GOEPC_STATE_OPENED_RSP;
+ }
+ else {
+ /* send failed, something error in transport layer, disconnect */
+ goepc_sm_act_disconnect(p_ccb);
+ }
+}
+
+static void goepc_sm_act_rsp(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
+{
+ /* handle srm state transfer */
+ BOOLEAN srm_en = FALSE;
+ BOOLEAN srm_wait = FALSE;
+ goepc_extra_srm_rsp(p_ccb->last_pkt_opcode, pkt, &srm_en, &srm_wait);
+ goepc_srm_sm_execute(p_ccb, FALSE, srm_en, srm_wait);
+ /* main state machine not change */
+
+ tGOEPC_MSG msg;
+ msg.response.opcode = p_ccb->last_pkt_opcode;
+ msg.response.final = FALSE;
+ msg.response.srm_en = (p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE_WAIT || p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE);
+ msg.response.srm_wait = (p_ccb->srm_state == GOEPC_SRM_STATE_ENABLE_WAIT);
+ msg.response.pkt = pkt;
+ p_ccb->callback(p_ccb->allocated, GOEPC_RESPONSE_EVT, &msg);
+}
+
+static void goepc_sm_act_rsp_fb(tGOEPC_CCB *p_ccb, BT_HDR *pkt)
+{
+ tGOEPC_MSG msg;
+ msg.response.opcode = p_ccb->last_pkt_opcode;
+ msg.response.final = TRUE;
+ msg.response.srm_en = FALSE;
+ msg.response.srm_wait = FALSE;
+ msg.response.pkt = pkt;
+ /* operation complete, reset srm state */
+ p_ccb->srm_state = GOEPC_SRM_STATE_IDLE;
+ /* main state machine transfer to OPENED_IDLE */
+ p_ccb->state = GOEPC_STATE_OPENED_IDLE;
+ p_ccb->callback(p_ccb->allocated, GOEPC_RESPONSE_EVT, &msg);
+}
+
+
+static void goepc_sm_state_opening(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
+{
+ switch (event)
+ {
+ case GOEPC_SM_EVENT_CONNECT:
+ goepc_sm_act_connect(p_ccb, &p_data->connected);
+ break;
+ case GOEPC_SM_EVENT_DISCONNECT:
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ case GOEPC_SM_EVENT_RSP:
+ case GOEPC_SM_EVENT_RSP_FB:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opening received unexpected response from peer\n");
+ if (p_data->pkt != NULL) {
+ osi_free(p_data->pkt);
+ }
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ default:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opening unexpected event: 0x%x\n", event);
+ break;
+ }
+}
+
+static void goepc_sm_state_opened_idle(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
+{
+ switch (event)
+ {
+ case GOEPC_SM_EVENT_DISCONNECT:
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ case GOEPC_SM_EVENT_REQ:
+ goepc_sm_act_send_req(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_REQ_FB:
+ goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_RSP:
+ case GOEPC_SM_EVENT_RSP_FB:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opened_idle received unexpected response from peer\n");
+ /* peer sent a packet to us when we didn't request */
+ if (p_data->pkt != NULL) {
+ osi_free(p_data->pkt);
+ }
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ default:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opened_idle unexpected event: 0x%x\n", event);
+ break;
+ }
+}
+
+static void goepc_sm_state_opened_req(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
+{
+ switch (event)
+ {
+ case GOEPC_SM_EVENT_DISCONNECT:
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ case GOEPC_SM_EVENT_REQ:
+ goepc_sm_act_send_req(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_REQ_FB:
+ goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_RSP:
+ goepc_sm_act_rsp(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_RSP_FB:
+ goepc_sm_act_rsp_fb(p_ccb, p_data->pkt);
+ break;
+ default:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opened_req unexpected event: 0x%x\n", event);
+ break;
+ }
+}
+
+static void goepc_sm_state_opened_rsp(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
+{
+ switch (event)
+ {
+ case GOEPC_SM_EVENT_DISCONNECT:
+ goepc_sm_act_disconnect(p_ccb);
+ break;
+ case GOEPC_SM_EVENT_REQ_FB:
+ goepc_sm_act_send_req_fb(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_RSP:
+ goepc_sm_act_rsp(p_ccb, p_data->pkt);
+ break;
+ case GOEPC_SM_EVENT_RSP_FB:
+ goepc_sm_act_rsp_fb(p_ccb, p_data->pkt);
+ break;
+ default:
+ GOEPC_TRACE_ERROR("goepc_sm_state_opened_rsp unexpected event: 0x%x\n", event);
+ break;
+ }
+}
+
+BOOLEAN goepc_check_obex_req_allow(UINT8 state, BOOLEAN final)
+{
+ BOOLEAN ret = FALSE;
+ if (final) {
+ switch (state)
+ {
+ case GOEPC_STATE_OPENED_IDLE:
+ case GOEPC_STATE_OPENED_REQ:
+ case GOEPC_STATE_OPENED_RSP:
+ ret = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ else {
+ switch (state)
+ {
+ case GOEPC_STATE_OPENED_IDLE:
+ case GOEPC_STATE_OPENED_REQ:
+ ret = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+void goepc_sm_execute(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data)
+{
+ switch (p_ccb->state)
+ {
+ case GOEPC_STATE_INIT:
+ /* do nothing */
+ break;
+ case GOEPC_STATE_OPENING:
+ goepc_sm_state_opening(p_ccb, event, p_data);
+ break;
+ case GOEPC_STATE_OPENED_IDLE:
+ goepc_sm_state_opened_idle(p_ccb, event, p_data);
+ break;
+ case GOEPC_STATE_OPENED_REQ:
+ goepc_sm_state_opened_req(p_ccb, event, p_data);
+ break;
+ case GOEPC_STATE_OPENED_RSP:
+ goepc_sm_state_opened_rsp(p_ccb, event, p_data);
+ break;
+ default:
+ GOEPC_TRACE_ERROR("goepc_sm_execute unexpected state: 0x%x\n", p_ccb->state);
+ break;
+ }
+}
+
+static void goepc_srm_sm_act_req(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_wait)
+{
+ switch (p_ccb->srm_state)
+ {
+ case GOEPC_SRM_STATE_IDLE:
+ if (srm_en) {
+ p_ccb->srm_state = GOEPC_SRM_STATE_REQ;
+ p_ccb->srm_wait = srm_wait;
+ }
+ else {
+ p_ccb->srm_state = GOEPC_SRM_STATE_DISABLE;
+ }
+ break;
+ case GOEPC_SRM_STATE_ENABLE_WAIT:
+ if (!srm_wait) {
+ p_ccb->srm_wait = FALSE;
+ }
+ if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) {
+ /* no more wait, transfer to ENABLE */
+ p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void goepc_srm_sm_act_rsp(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_wait)
+{
+ switch (p_ccb->srm_state)
+ {
+ case GOEPC_SRM_STATE_IDLE:
+ /* peer can not request to enable srm, ignore */
+ break;
+ case GOEPC_SRM_STATE_REQ:
+ if (srm_en) {
+ p_ccb->srm_peer_wait = srm_wait;
+ if (p_ccb->srm_wait || p_ccb->srm_peer_wait) {
+ p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE_WAIT;
+ }
+ else {
+ p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
+ }
+ }
+ else {
+ p_ccb->srm_state = GOEPC_SRM_STATE_DISABLE;
+ }
+ break;
+ case GOEPC_SRM_STATE_ENABLE_WAIT:
+ if (!srm_wait) {
+ p_ccb->srm_peer_wait = FALSE;
+ }
+ if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) {
+ /* no more wait, transfer to ENABLE */
+ p_ccb->srm_state = GOEPC_SRM_STATE_ENABLE;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void goepc_srm_sm_execute(tGOEPC_CCB *p_ccb, BOOLEAN is_req, BOOLEAN srm_en, BOOLEAN srm_wait)
+{
+ if (is_req) {
+ goepc_srm_sm_act_req(p_ccb, srm_en, srm_wait);
+ }
+ else {
+ goepc_srm_sm_act_rsp(p_ccb, srm_en, srm_wait);
+ }
+}
+
+#endif /* #if (GOEPC_INCLUDED == TRUE) */
diff --git a/lib/bt/host/bluedroid/stack/goep/include/goep_int.h b/lib/bt/host/bluedroid/stack/goep/include/goep_int.h
new file mode 100644
index 00000000..0c1915f6
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/goep/include/goep_int.h
@@ -0,0 +1,103 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#pragma once
+
+#include "common/bt_target.h"
+
+#include "stack/obex_api.h"
+#include "stack/goep_common.h"
+#include "stack/goepc_api.h"
+
+#if (GOEPC_INCLUDED == TRUE)
+
+/* GOEPC state machine events */
+enum {
+ GOEPC_SM_EVENT_CONNECT = 0,
+ GOEPC_SM_EVENT_DISCONNECT,
+ GOEPC_SM_EVENT_REQ,
+ GOEPC_SM_EVENT_REQ_FB,
+ GOEPC_SM_EVENT_RSP,
+ GOEPC_SM_EVENT_RSP_FB,
+};
+
+/* GOEPC state machine states */
+enum {
+ GOEPC_STATE_INIT = 0,
+ GOEPC_STATE_OPENING,
+ GOEPC_STATE_OPENED_IDLE,
+ GOEPC_STATE_OPENED_REQ,
+ GOEPC_STATE_OPENED_RSP,
+};
+
+/* GOEPC srm state machine states */
+enum {
+ GOEPC_SRM_STATE_IDLE = 0,
+ GOEPC_SRM_STATE_REQ,
+ GOEPC_SRM_STATE_ENABLE_WAIT,
+ GOEPC_SRM_STATE_ENABLE,
+ GOEPC_SRM_STATE_DISABLE,
+};
+
+/* GOEPC Connection Control block */
+typedef struct {
+ tGOEPC_EVT_CBACK *callback; /* GOEP event callback function */
+ UINT16 obex_handle; /* OBEX connection handle */
+ UINT16 peer_mtu; /* lower layer connection peer MTU */
+ UINT16 our_mtu; /* lower layer connection our MTU */
+ BOOLEAN congest; /* lower layer connection congestion status */
+
+ BT_HDR *pkt; /* packet prepared in this GOEP client */
+ BOOLEAN pkt_srm_en; /* whether prepared packet had set SRM to enable */
+ BOOLEAN pkt_srm_wait; /* whether prepared packet had set SRMP to wait */
+ UINT8 curr_pkt_opcode; /* prepared packet opcode */
+
+ UINT8 last_pkt_opcode; /* last sent packet opcode */
+ BOOLEAN srm_wait; /* whether we had set SRMP to wait */
+ BOOLEAN srm_peer_wait; /* whether peer had set SRMP to wait */
+ UINT8 srm_state; /* SRM state machine */
+ UINT8 state; /* main state machine */
+ UINT8 allocated; /* 0, not allocated. index+1, otherwise. equal to api handle */
+} tGOEPC_CCB;
+
+/* GOEPC Control block */
+typedef struct {
+ tGOEPC_CCB ccb[GOEPC_MAX_CONNECTION]; /* connection control blocks */
+ UINT8 trace_level; /* trace level */
+} tGOEPC_CB;
+
+#if GOEP_DYNAMIC_MEMORY == FALSE
+extern tGOEPC_CB goepc_cb;
+#else
+extern tGOEPC_CB *goepc_cb_ptr;
+#define goepc_cb (*goepc_cb_ptr)
+#endif
+
+typedef struct {
+ UINT16 peer_mtu;
+ UINT16 our_mtu;
+} tGOEPC_CONNECTED;
+
+typedef struct {
+ UINT16 peer_mtu;
+ UINT16 our_mtu;
+} tGOEPC_MTU_CHG;
+
+typedef union {
+ tGOEPC_CONNECTED connected;
+ tGOEPC_MTU_CHG mtu_chg;
+ BT_HDR *pkt;
+} tGOEPC_DATA;
+
+tGOEPC_CCB *goepc_allocate_ccb(void);
+void goepc_free_ccb(tGOEPC_CCB *p_ccb);
+void goepc_obex_callback(UINT16 handle, UINT8 event, tOBEX_MSG *msg);
+BOOLEAN goepc_check_obex_req_allow(UINT8 state, BOOLEAN final);
+BOOLEAN goepc_check_obex_req_param(tOBEX_PARSE_INFO *info);
+void goepc_sm_execute(tGOEPC_CCB *p_ccb, UINT8 event, tGOEPC_DATA *p_data);
+void goepc_srm_sm_execute(tGOEPC_CCB *p_ccb, BOOLEAN is_req, BOOLEAN srm_en, BOOLEAN srm_wait);
+
+#endif /* #if (GOEPC_INCLUDED == TRUE) */