From d7298a71c3194154770cf69d25f78fc89b5692c5 Mon Sep 17 00:00:00 2001 From: linruihao Date: Fri, 9 Aug 2024 15:56:13 +0800 Subject: [PATCH] feat(bt/bluedroid): Support GOEP Client basic feature --- .../common/include/common/bt_target.h | 11 + .../bt/host/bluedroid/stack/goep/goepc_api.c | 376 +++++++++++++ .../bt/host/bluedroid/stack/goep/goepc_main.c | 528 ++++++++++++++++++ .../bluedroid/stack/goep/include/goep_int.h | 103 ++++ .../stack/include/stack/goep_common.h | 18 + .../bluedroid/stack/include/stack/goepc_api.h | 82 +++ 6 files changed, 1118 insertions(+) create mode 100644 components/bt/host/bluedroid/stack/goep/goepc_api.c create mode 100644 components/bt/host/bluedroid/stack/goep/goepc_main.c create mode 100644 components/bt/host/bluedroid/stack/goep/include/goep_int.h create mode 100644 components/bt/host/bluedroid/stack/include/stack/goep_common.h create mode 100644 components/bt/host/bluedroid/stack/include/stack/goepc_api.h diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 4310bd162a..032d38eb41 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -1819,6 +1819,17 @@ #define OBEX_MAX_SERVER 2 #endif +/****************************************************************************** +** +** GOEP +** +******************************************************************************/ + +/* Maximum GOEP client connection allowed */ +#ifndef GOEPC_MAX_CONNECTION +#define GOEPC_MAX_CONNECTION 3 +#endif + /****************************************************************************** ** ** BNEP diff --git a/components/bt/host/bluedroid/stack/goep/goepc_api.c b/components/bt/host/bluedroid/stack/goep/goepc_api.c new file mode 100644 index 0000000000..60badbaeec --- /dev/null +++ b/components/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 + +#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) { + 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/components/bt/host/bluedroid/stack/goep/goepc_main.c b/components/bt/host/bluedroid/stack/goep/goepc_main.c new file mode 100644 index 0000000000..e0859580ec --- /dev/null +++ b/components/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 + +#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/components/bt/host/bluedroid/stack/goep/include/goep_int.h b/components/bt/host/bluedroid/stack/goep/include/goep_int.h new file mode 100644 index 0000000000..0c1915f6d6 --- /dev/null +++ b/components/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) */ diff --git a/components/bt/host/bluedroid/stack/include/stack/goep_common.h b/components/bt/host/bluedroid/stack/include/stack/goep_common.h new file mode 100644 index 0000000000..82b315691e --- /dev/null +++ b/components/bt/host/bluedroid/stack/include/stack/goep_common.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "common/bt_target.h" + +#define GOEP_SUCCESS 0 /* Operation successful */ +#define GOEP_FAILURE 1 /* Operation failed */ +#define GOEP_NO_RESOURCES 3 /* Not enough resources */ +#define GOEP_BAD_HANDLE 4 /* Bad handle */ +#define GOEP_INVALID_PARAM 5 /* Invalid parameter */ +#define GOEP_INVALID_STATE 6 /* Operation not allow in current state */ +#define GOEP_CONGEST 7 /* Congest */ +#define GOEP_TL_ERROR 8 /* Lower transport layer error */ diff --git a/components/bt/host/bluedroid/stack/include/stack/goepc_api.h b/components/bt/host/bluedroid/stack/include/stack/goepc_api.h new file mode 100644 index 0000000000..f2b93f86b0 --- /dev/null +++ b/components/bt/host/bluedroid/stack/include/stack/goepc_api.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "common/bt_target.h" + +#include "stack/goep_common.h" +#include "stack/obex_api.h" + +#if (GOEPC_INCLUDED == TRUE) + +enum { + GOEPC_OPENED_EVT, /* connection open */ + GOEPC_CLOSED_EVT, /* disconnect unexpected */ + GOEPC_MTU_CHANGED_EVT, /* lower layer MTU change */ + GOEPC_CONGEST_EVT, /* lower layer connection congest */ + GOEPC_UNCONGEST_EVT, /* lower layer connection uncongest */ + GOEPC_RESPONSE_EVT /* response from server */ +}; + +typedef struct { + UINT16 peer_mtu; /* peer mtu of lower level connection */ + UINT16 our_mtu; /* our mtu of lower level connection */ +} tGOEPC_MSG_OPENED; + +typedef struct { + UINT8 reason; /* connection close reason */ +} tGOEPC_MSG_CLOSED; + +typedef struct { + UINT16 peer_mtu; /* peer mtu of lower level connection */ + UINT16 our_mtu; /* our mtu of lower level connection */ +} tGOEPC_MSG_MTU_CHANGED; + +typedef struct { + UINT8 opcode; /* which opcode that this packet response to */ + BOOLEAN final; /* whether this is a final packet */ + BOOLEAN srm_en; /* whether srm is enable */ + BOOLEAN srm_wait; /* whether srm wait is set, set by peer or by us */ + BT_HDR *pkt; /* pointer to response packet */ +} tGOEPC_MSG_RESPONSE; + +typedef union { + tGOEPC_MSG_OPENED opened; + tGOEPC_MSG_CLOSED closed; + tGOEPC_MSG_MTU_CHANGED mtu_changed; + tGOEPC_MSG_RESPONSE response; +} tGOEPC_MSG; + +typedef void (tGOEPC_EVT_CBACK)(UINT16 handle, UINT8 event, tGOEPC_MSG *msg); + +/******************************************************************************* +* The following APIs are called by bluetooth stack automatically +*******************************************************************************/ + +extern UINT16 GOEPC_Init(void); + +extern void GOEPC_Deinit(void); + +/******************************************************************************* +* The following APIs must be executed in btu task +*******************************************************************************/ + +extern UINT16 GOEPC_Open(tOBEX_SVR_INFO *p_svr, tGOEPC_EVT_CBACK callback, UINT16 *out_handle); + +extern UINT16 GOEPC_Close(UINT16 handle); + +extern UINT16 GOEPC_SendRequest(UINT16 handle); + +extern UINT16 GOEPC_PrepareRequest(UINT16 handle, tOBEX_PARSE_INFO *info, UINT16 buff_size); + +extern UINT16 GOEPC_DropRequest(UINT16 handle); + +extern UINT16 GOEPC_RequestSetSRM(UINT16 handle, BOOLEAN srm_en, BOOLEAN srm_wait); + +extern UINT16 GOEPC_RequestAddHeader(UINT16 handle, UINT8 header_id, const UINT8 *data, UINT16 data_len); + +#endif /* #if (GOEPC_INCLUDED == TRUE) */