From 395f5d94048f02a6b7d6f0c8c37fa10176bbc6e9 Mon Sep 17 00:00:00 2001 From: hejiaxin Date: Fri, 26 Jun 2026 14:57:31 +0800 Subject: [PATCH] fix(bt_obex): fix some bugs in bluedroid obex - fix goepc wait state switch - fix psm unbind's mismatch - fix some resources leak - fix goep connection's incorrect ternimation when congesting - Add some NULL ptr check --- .../bt/host/bluedroid/bta/av/bta_av_ca_act.c | 4 +- .../bt/host/bluedroid/stack/goep/goepc_api.c | 9 +- .../bt/host/bluedroid/stack/goep/goepc_main.c | 6 +- .../bt/host/bluedroid/stack/obex/obex_api.c | 36 +- .../bt/host/bluedroid/stack/obex/obex_main.c | 40 +- .../host/bluedroid/stack/obex/obex_tl_l2cap.c | 166 ++++--- .../bluedroid/stack/obex/obex_tl_rfcomm.c | 456 ++++++++++++++++++ 7 files changed, 632 insertions(+), 85 deletions(-) create mode 100644 components/bt/host/bluedroid/stack/obex/obex_tl_rfcomm.c diff --git a/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c b/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c index 86f1ff507ad..9a9e51a24e7 100644 --- a/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c +++ b/components/bt/host/bluedroid/bta/av/bta_av_ca_act.c @@ -162,7 +162,9 @@ void bta_av_ca_goep_event_handler(UINT16 handle, UINT8 event, tGOEPC_MSG *p_msg) tBTA_AV_DATA *p_data = NULL; UINT16 rcb_idx; - if (p_msg == NULL) { + if (p_msg == NULL && (event == GOEPC_OPENED_EVT || + event == GOEPC_CLOSED_EVT || + event == GOEPC_RESPONSE_EVT)) { goto error; } diff --git a/components/bt/host/bluedroid/stack/goep/goepc_api.c b/components/bt/host/bluedroid/stack/goep/goepc_api.c index c34112314f6..db7528143bc 100644 --- a/components/bt/host/bluedroid/stack/goep/goepc_api.c +++ b/components/bt/host/bluedroid/stack/goep/goepc_api.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -54,6 +54,13 @@ void GOEPC_Deinit(void) { #if (GOEP_DYNAMIC_MEMORY) if (goepc_cb_ptr) { +#endif + for (int i = 0; i < GOEPC_MAX_CONNECTION; ++i) { + if (goepc_cb.ccb[i].allocated) { + GOEPC_Close(goepc_cb.ccb[i].allocated); + } + } +#if (GOEP_DYNAMIC_MEMORY) osi_free(goepc_cb_ptr); goepc_cb_ptr = NULL; } diff --git a/components/bt/host/bluedroid/stack/goep/goepc_main.c b/components/bt/host/bluedroid/stack/goep/goepc_main.c index b5222f494b2..ac485c36b65 100644 --- a/components/bt/host/bluedroid/stack/goep/goepc_main.c +++ b/components/bt/host/bluedroid/stack/goep/goepc_main.c @@ -231,7 +231,7 @@ static void goepc_sm_act_connect(tGOEPC_CCB *p_ccb, tGOEPC_CONNECTED *connected) static void goepc_sm_act_disconnect(tGOEPC_CCB *p_ccb) { - tGOEPC_MSG msg; + tGOEPC_MSG msg = {0}; if (p_ccb->obex_handle) { OBEX_RemoveConn(p_ccb->obex_handle); } @@ -510,7 +510,7 @@ static void goepc_srm_sm_act_req(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_ } break; case GOEPC_SRM_STATE_ENABLE_WAIT: - if (!srm_wait) { + if (!srm_wait){ p_ccb->srm_wait = FALSE; } if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) { @@ -545,7 +545,7 @@ static void goepc_srm_sm_act_rsp(tGOEPC_CCB *p_ccb, BOOLEAN srm_en, BOOLEAN srm_ } break; case GOEPC_SRM_STATE_ENABLE_WAIT: - if (!srm_wait) { + if (!srm_wait){ p_ccb->srm_peer_wait = FALSE; } if (!p_ccb->srm_wait && !p_ccb->srm_peer_wait) { diff --git a/components/bt/host/bluedroid/stack/obex/obex_api.c b/components/bt/host/bluedroid/stack/obex/obex_api.c index 929ab155b4b..6db6e75bae3 100644 --- a/components/bt/host/bluedroid/stack/obex/obex_api.c +++ b/components/bt/host/bluedroid/stack/obex/obex_api.c @@ -135,6 +135,10 @@ UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 tOBEX_TL_SVR_INFO tl_server = {0}; obex_server_to_tl_server(server, &tl_server); p_ccb->tl = server->tl; + if (obex_cb.tl_ops[p_ccb->tl] == NULL){ + ret = OBEX_ERROR_TL; + break; + } p_ccb->tl_hdl = obex_cb.tl_ops[p_ccb->tl]->connect(&tl_server); if (p_ccb->tl_hdl == 0) { ret = OBEX_ERROR_TL; @@ -195,7 +199,7 @@ UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UIN tOBEX_SCB *p_scb = NULL; do { - if (server->tl >= OBEX_NUM_TL) { + if (!server || server->tl >= OBEX_NUM_TL || obex_cb.tl_ops[server->tl] == NULL) { ret = OBEX_INVALID_PARAM; break; } @@ -322,12 +326,31 @@ UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt) *******************************************************************************/ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt) { - if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) { + if (info == NULL || out_pkt == NULL) { return OBEX_INVALID_PARAM; } if (UINT16_MAX - buff_size < sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET) { return OBEX_NO_RESOURCES; } + switch (info->opcode) + { + case OBEX_OPCODE_CONNECT: + if (buff_size < OBEX_MIN_PACKET_SIZE + 4){ + return OBEX_INVALID_PARAM; + } + break; + case OBEX_OPCODE_SETPATH: + if (buff_size < OBEX_MIN_PACKET_SIZE + 2){ + return OBEX_INVALID_PARAM; + } + break; + default: + if (buff_size < OBEX_MIN_PACKET_SIZE){ + return OBEX_INVALID_PARAM; + } + break; + } + buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET; BT_HDR *p_buf = (BT_HDR *)osi_malloc(buff_size); @@ -469,7 +492,7 @@ UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header) return OBEX_INVALID_PARAM; } - if (pkt->layer_specific - pkt->len < header_len) { + if (pkt->layer_specific <= pkt->len || pkt->layer_specific - pkt->len < header_len) { /* the packet can not hold this header */ return OBEX_NO_RESOURCES; } @@ -510,6 +533,9 @@ UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UIN case OBEX_HEADER_ID_U2B_TYPE1: case OBEX_HEADER_ID_U2B_TYPE2: /* header id + 2 byte length prefixed + data */ + if (data_len > UINT16_MAX - 3){ + return OBEX_NO_RESOURCES; + } header_len = data_len + 3; store_header_len = TRUE; break; @@ -532,7 +558,7 @@ UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UIN return OBEX_INVALID_PARAM; } - if (pkt->layer_specific - pkt->len < header_len) { + if (pkt->layer_specific <= pkt->len || pkt->layer_specific - pkt->len < header_len) { /* the packet can not hold this header */ return OBEX_NO_RESOURCES; } @@ -596,7 +622,7 @@ UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value) *******************************************************************************/ UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt) { - if (pkt == NULL) { + if (pkt == NULL || pkt->layer_specific < pkt->len) { return 0; } return pkt->layer_specific - pkt->len; diff --git a/components/bt/host/bluedroid/stack/obex/obex_main.c b/components/bt/host/bluedroid/stack/obex/obex_main.c index c5f04539021..9c1a90a431f 100644 --- a/components/bt/host/bluedroid/stack/obex/obex_main.c +++ b/components/bt/host/bluedroid/stack/obex/obex_main.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +16,8 @@ #if (OBEX_INCLUDED == TRUE) +_Static_assert(OBEX_MAX_SERVER <= 254, "OBEX_MAX_SERVER must not exceed 254 to prevent overflow."); + #if OBEX_DYNAMIC_MEMORY == FALSE tOBEX_CB obex_cb; #else @@ -88,12 +90,14 @@ void obex_free_scb(tOBEX_SCB *p_scb) static bool check_conn_mtu_valid(tOBEX_CCB *p_ccb, BOOLEAN call_cb) { if (p_ccb->tl_our_mtu < 255 || p_ccb->tl_peer_mtu < 255) { - if (call_cb && p_ccb->callback) { - p_ccb->callback(p_ccb->allocated, OBEX_DISCONNECT_EVT, NULL); - } - OBEX_TRACE_ERROR("Check OBEX transport layer MTU failed, disconnect"); + tOBEX_MSG_CBACK *cb = p_ccb->callback; + UINT8 alloc = p_ccb->allocated; obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl); obex_free_ccb(p_ccb); + if (call_cb && cb) { + cb(alloc, OBEX_DISCONNECT_EVT, NULL); + } + OBEX_TRACE_ERROR("Check OBEX transport layer MTU failed, disconnect"); return false; } return true; @@ -109,7 +113,9 @@ void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) switch (evt) { case OBEX_TL_CONN_OPEN_EVT: - assert(p_ccb != NULL); + if (p_ccb == NULL) { + break; + } p_ccb->tl_peer_mtu = msg->conn_open.peer_mtu; p_ccb->tl_our_mtu = msg->conn_open.our_mtu; if (!check_conn_mtu_valid(p_ccb, TRUE)) { @@ -131,19 +137,25 @@ void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) } break; case OBEX_TL_CONGEST_EVT: - assert(p_ccb != NULL); + if (p_ccb == NULL) { + break; + } if (p_ccb->callback) { p_ccb->callback(p_ccb->allocated, OBEX_CONGEST_EVT, NULL); } break; case OBEX_TL_UNCONGEST_EVT: - assert(p_ccb != NULL); + if (p_ccb == NULL) { + break; + } if (p_ccb->callback) { p_ccb->callback(p_ccb->allocated, OBEX_UNCONGEST_EVT, NULL); } break; case OBEX_TL_MTU_CHANGE_EVT: - assert(p_ccb != NULL); + if (p_ccb == NULL) { + break; + } p_ccb->tl_peer_mtu = msg->mtu_chg.peer_mtu; p_ccb->tl_our_mtu = msg->mtu_chg.our_mtu; if (!check_conn_mtu_valid(p_ccb, TRUE)) { @@ -156,7 +168,11 @@ void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) } break; case OBEX_TL_DATA_EVT: - assert(p_ccb != NULL); + if (p_ccb == NULL) { + OBEX_TRACE_WARNING("Received OBEX_TL_DATA_EVT but CCB is NULL, dropping packet."); + osi_free(msg->data.p_buf); + break; + } if (p_ccb->callback) { cb_msg.data.pkt = msg->data.p_buf; p_ccb->callback(p_ccb->allocated, OBEX_DATA_EVT, &cb_msg); @@ -168,7 +184,9 @@ void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) break; case OBEX_TL_CONN_INCOME_EVT: /* New connection, p_ccb should be NULL */ - assert(p_ccb == NULL); + if (p_ccb != NULL) { + break; + } p_scb = obex_find_scb_by_tl_hdl(tl, msg->conn_income.svr_hdl); if (p_scb == NULL) { obex_cb.tl_ops[tl]->disconnect(tl_hdl); diff --git a/components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c b/components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c index 46bbda4417e..f59ebfbf65a 100644 --- a/components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c +++ b/components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c @@ -139,7 +139,8 @@ static tOBEX_TL_L2CAP_CCB *find_ccb_by_psm(UINT16 psm) { tOBEX_TL_L2CAP_CCB *p_ccb = NULL; for(int i = 0; i < OBEX_TL_L2CAP_NUM_CONN; ++i) { - if (obex_tl_l2cap_cb.ccb[i].allocated && obex_tl_l2cap_cb.ccb[i].vpsm == psm) { + if (obex_tl_l2cap_cb.ccb[i].allocated + && obex_tl_l2cap_cb.ccb[i].vpsm == psm) { p_ccb = &obex_tl_l2cap_cb.ccb[i]; break; } @@ -201,6 +202,11 @@ static void l2cap_sec_check_complete_term(BD_ADDR bd_addr, tBT_TRANSPORT transpo { tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data; + if (p_ccb->allocated == 0 || memcmp(p_ccb->addr, bd_addr, BD_ADDR_LEN) != 0) { + OBEX_TL_L2CAP_TRACE_WARNING("CCB invalid or reallocated to another device, ignore sec_check_complete\n"); + return; + } + if (res == BTM_SUCCESS) { L2CA_ErtmConnectRsp(p_ccb->addr, p_ccb->id, p_ccb->lcid, L2CAP_CONN_OK, 0, &obex_tl_l2cap_etm_opts); tL2CAP_CFG_INFO cfg = {0}; @@ -214,8 +220,8 @@ static void l2cap_sec_check_complete_term(BD_ADDR bd_addr, tBT_TRANSPORT transpo L2CA_ErtmConnectRsp(p_ccb->addr, p_ccb->id, p_ccb->lcid, L2CAP_CONN_SECURITY_BLOCK, 0, &obex_tl_l2cap_etm_opts); tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); } } @@ -231,6 +237,11 @@ static void l2cap_sec_check_complete_orig(BD_ADDR bd_addr, tBT_TRANSPORT transpo { tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data; + if (p_ccb->allocated == 0 || memcmp(p_ccb->addr, bd_addr, BD_ADDR_LEN) != 0) { + OBEX_TL_L2CAP_TRACE_WARNING("CCB invalid or reallocated to another device, ignore sec_check_complete\n"); + return; + } + if (res == BTM_SUCCESS) { tL2CAP_CFG_INFO cfg = {0}; cfg.mtu_present = TRUE; @@ -242,8 +253,8 @@ static void l2cap_sec_check_complete_orig(BD_ADDR bd_addr, tBT_TRANSPORT transpo L2CA_DisconnectReq(p_ccb->lcid); tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); } } @@ -312,8 +323,8 @@ void obex_tl_l2cap_connect_cfm(UINT16 lcid, UINT16 result) OBEX_TL_L2CAP_TRACE_WARNING("l2cap_connect_cfm result != L2CAP_CONN_OK: result: 0x%x\n", result); tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); } } @@ -412,6 +423,11 @@ void obex_tl_l2cap_config_cfm(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) if (p_ccb->initiator == FALSE && p_scb == NULL) { /* not a initiator, but can not find corresponding server */ OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_cfm, not a initiator, but can not find corresponding server\n"); + L2CA_DisconnectReq(p_ccb->lcid); + tOBEX_TL_MSG msg = {0}; + msg.any.hdl = p_ccb->allocated; + free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); return; } @@ -420,8 +436,12 @@ void obex_tl_l2cap_config_cfm(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) L2CA_DisconnectReq(p_ccb->lcid); tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); + return; + } + + if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED) { return; } @@ -490,8 +510,8 @@ void obex_tl_l2cap_disconnect_ind(UINT16 lcid, BOOLEAN is_conf_needed) tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); } @@ -545,63 +565,6 @@ void obex_tl_l2cap_congestion_status_ind(UINT16 lcid, BOOLEAN is_congested) } } -/******************************************************************************* -** -** Function obex_tl_l2cap_init -** -** Description Initialize OBEX over L2CAP transport layer, callback -** can not be NULL, must be called once before using any -** other APIs -** -*******************************************************************************/ -UINT16 obex_tl_l2cap_init(tOBEX_TL_CBACK *callback) -{ - assert(callback != NULL); -#if (OBEX_DYNAMIC_MEMORY) - if (!obex_tl_l2cap_cb_ptr) { - obex_tl_l2cap_cb_ptr = (tOBEX_TL_L2CAP_CB *)osi_malloc(sizeof(tOBEX_TL_L2CAP_CB)); - if (!obex_tl_l2cap_cb_ptr) { - OBEX_TL_L2CAP_TRACE_ERROR("OBEX over L2CAP transport layer initialize failed, no memory\n"); - return OBEX_TL_FAILED; - } - } -#endif /* #if (OBEX_DYNAMIC_MEMORY) */ - memset(&obex_tl_l2cap_cb, 0, sizeof(tOBEX_TL_L2CAP_CB)); - obex_tl_l2cap_cb.callback = callback; - obex_tl_l2cap_cb.trace_level = BT_TRACE_LEVEL_ERROR; - - tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info; - - p_reg_info->pL2CA_ConnectInd_Cb = NULL; /* obex_tl_l2cap_connect_ind or NULL, depend on server or not */ - p_reg_info->pL2CA_ConnectCfm_Cb = obex_tl_l2cap_connect_cfm; - p_reg_info->pL2CA_ConnectPnd_Cb = NULL; - p_reg_info->pL2CA_ConfigInd_Cb = obex_tl_l2cap_config_ind; - p_reg_info->pL2CA_ConfigCfm_Cb = obex_tl_l2cap_config_cfm; - p_reg_info->pL2CA_DisconnectInd_Cb = obex_tl_l2cap_disconnect_ind; - p_reg_info->pL2CA_DisconnectCfm_Cb = NULL; - p_reg_info->pL2CA_QoSViolationInd_Cb = obex_tl_l2cap_qos_violation_ind; - p_reg_info->pL2CA_DataInd_Cb = obex_tl_l2cap_buf_data_ind; - p_reg_info->pL2CA_CongestionStatus_Cb = obex_tl_l2cap_congestion_status_ind; - p_reg_info->pL2CA_TxComplete_Cb = NULL; - return OBEX_TL_SUCCESS; -} - -/******************************************************************************* -** -** Function obex_tl_l2cap_deinit -** -** Description Deinitialize OBEX over L2CAP transport layer -** -*******************************************************************************/ -void obex_tl_l2cap_deinit(void) -{ -#if (OBEX_DYNAMIC_MEMORY) - if (obex_tl_l2cap_cb_ptr) { - osi_free(obex_tl_l2cap_cb_ptr); - obex_tl_l2cap_cb_ptr = NULL; - } -#endif /* #if (OBEX_DYNAMIC_MEMORY) */ -} /******************************************************************************* ** @@ -642,9 +605,14 @@ UINT16 obex_tl_l2cap_connect(tOBEX_TL_SVR_INFO *server) else { p_ccb->our_mtu = server->l2cap.pref_mtu; } + bdcpy(p_ccb->addr, server->l2cap.addr); p_ccb->initiator = TRUE; p_ccb->lcid = L2CA_ErtmConnectReq(p_ccb->vpsm, server->l2cap.addr, &obex_tl_l2cap_etm_opts); if (p_ccb->lcid == 0) { + /* outgoing-only PSM was registered above; unload it on connect failure */ + if (find_scb_by_psm(p_ccb->vpsm) == NULL) { + L2CA_Deregister(p_ccb->vpsm); + } free_ccb(p_ccb); return 0; } @@ -776,14 +744,84 @@ void obex_tl_l2cap_unbind(UINT16 tl_hdl) L2CA_DisconnectReq(p_ccb->lcid); tOBEX_TL_MSG msg = {0}; msg.any.hdl = p_ccb->allocated; - obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); free_ccb(p_ccb); + obex_tl_l2cap_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); } L2CA_Deregister(p_scb->psm); free_scb(p_scb); } } +/******************************************************************************* +** +** Function obex_tl_l2cap_init +** +** Description Initialize OBEX over L2CAP transport layer, callback +** can not be NULL, must be called once before using any +** other APIs +** +*******************************************************************************/ +UINT16 obex_tl_l2cap_init(tOBEX_TL_CBACK *callback) +{ + assert(callback != NULL); +#if (OBEX_DYNAMIC_MEMORY) + if (!obex_tl_l2cap_cb_ptr) { + obex_tl_l2cap_cb_ptr = (tOBEX_TL_L2CAP_CB *)osi_malloc(sizeof(tOBEX_TL_L2CAP_CB)); + if (!obex_tl_l2cap_cb_ptr) { + OBEX_TL_L2CAP_TRACE_ERROR("OBEX over L2CAP transport layer initialize failed, no memory\n"); + return OBEX_TL_FAILED; + } + } +#endif /* #if (OBEX_DYNAMIC_MEMORY) */ + memset(&obex_tl_l2cap_cb, 0, sizeof(tOBEX_TL_L2CAP_CB)); + obex_tl_l2cap_cb.callback = callback; + obex_tl_l2cap_cb.trace_level = BT_TRACE_LEVEL_ERROR; + + tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info; + + p_reg_info->pL2CA_ConnectInd_Cb = NULL; /* obex_tl_l2cap_connect_ind or NULL, depend on server or not */ + p_reg_info->pL2CA_ConnectCfm_Cb = obex_tl_l2cap_connect_cfm; + p_reg_info->pL2CA_ConnectPnd_Cb = NULL; + p_reg_info->pL2CA_ConfigInd_Cb = obex_tl_l2cap_config_ind; + p_reg_info->pL2CA_ConfigCfm_Cb = obex_tl_l2cap_config_cfm; + p_reg_info->pL2CA_DisconnectInd_Cb = obex_tl_l2cap_disconnect_ind; + p_reg_info->pL2CA_DisconnectCfm_Cb = NULL; + p_reg_info->pL2CA_QoSViolationInd_Cb = obex_tl_l2cap_qos_violation_ind; + p_reg_info->pL2CA_DataInd_Cb = obex_tl_l2cap_buf_data_ind; + p_reg_info->pL2CA_CongestionStatus_Cb = obex_tl_l2cap_congestion_status_ind; + p_reg_info->pL2CA_TxComplete_Cb = NULL; + return OBEX_TL_SUCCESS; +} + +/******************************************************************************* +** +** Function obex_tl_l2cap_deinit +** +** Description Deinitialize OBEX over L2CAP transport layer +** +*******************************************************************************/ +void obex_tl_l2cap_deinit(void) +{ +#if (OBEX_DYNAMIC_MEMORY) + if (obex_tl_l2cap_cb_ptr){ +#endif + for (int i = 0; i < OBEX_TL_L2CAP_NUM_CONN; ++i) { + if (obex_tl_l2cap_cb.ccb[i].allocated) { + obex_tl_l2cap_disconnect(obex_tl_l2cap_cb.ccb[i].allocated); + } + } + for (int i = 0; i < OBEX_TL_L2CAP_NUM_SERVER; ++i) { + if (obex_tl_l2cap_cb.scb[i].allocated) { + obex_tl_l2cap_unbind(obex_tl_l2cap_cb.scb[i].allocated << 8); + } + } +#if (OBEX_DYNAMIC_MEMORY) + osi_free(obex_tl_l2cap_cb_ptr); + obex_tl_l2cap_cb_ptr = NULL; + } +#endif /* #if (OBEX_DYNAMIC_MEMORY) */ +} + static tOBEX_TL_OPS obex_tl_l2cap_ops = { .init = obex_tl_l2cap_init, .deinit = obex_tl_l2cap_deinit, diff --git a/components/bt/host/bluedroid/stack/obex/obex_tl_rfcomm.c b/components/bt/host/bluedroid/stack/obex/obex_tl_rfcomm.c new file mode 100644 index 00000000000..3a5714a19e7 --- /dev/null +++ b/components/bt/host/bluedroid/stack/obex/obex_tl_rfcomm.c @@ -0,0 +1,456 @@ +/* + * SPDX-FileCopyrightText: 2024-2026 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/port_api.h" +#include "stack/btm_api.h" +#include "stack/sdpdefs.h" +#include "obex_tl.h" +#include "obex_tl_rfcomm.h" + +#if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) + +#define OBEX_TL_RFCOMM_NUM_CONN 4 +#define OBEX_TL_RFCOMM_NUM_SERVER 2 + +#define OBEX_TL_RFCOMM_EVENT_MARK (PORT_EV_FC | PORT_EV_FCS) + +typedef struct { + UINT16 rfc_handle; /* rfcomm handle */ + UINT16 mtu; /* rfcomm mtu */ + BOOLEAN initiator; /* TRUE if is initiator, otherwise FALSE */ + UINT8 scn; /* service channel number */ + BD_ADDR addr; /* peer bluetooth device address */ + UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, equal to handle */ +} tOBEX_TL_RFCOMM_CCB; + +typedef struct { + UINT16 rfc_handle; /* rfcomm handle */ + UINT8 scn; /* service channel number */ + UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, handle of server will left shift 8 bits */ +} tOBEX_TL_RFCOMM_SCB; + +typedef struct { + tOBEX_TL_CBACK *callback; /* Upper layer callback */ + tOBEX_TL_RFCOMM_CCB ccb[OBEX_TL_RFCOMM_NUM_CONN]; + tOBEX_TL_RFCOMM_SCB scb[OBEX_TL_RFCOMM_NUM_SERVER]; + UINT8 trace_level; /* trace level */ +} tOBEX_TL_RFCOMM_CB; + +#if OBEX_DYNAMIC_MEMORY == FALSE +static tOBEX_TL_RFCOMM_CB obex_tl_rfcomm_cb; +#else +static tOBEX_TL_RFCOMM_CB *obex_tl_rfcomm_cb_ptr = NULL; +#define obex_tl_rfcomm_cb (*obex_tl_rfcomm_cb_ptr) +#endif + +static tOBEX_TL_RFCOMM_CCB *allocate_ccb(void) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = NULL; + for(int i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; ++i) { + if (obex_tl_rfcomm_cb.ccb[i].allocated == 0) { + obex_tl_rfcomm_cb.ccb[i].allocated = i + 1; + p_ccb = &obex_tl_rfcomm_cb.ccb[i]; + break; + } + } + return p_ccb; +} + +static tOBEX_TL_RFCOMM_SCB *allocate_scb(void) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = NULL; + for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) { + if (obex_tl_rfcomm_cb.scb[i].allocated == 0) { + obex_tl_rfcomm_cb.scb[i].allocated = i + 1; + p_scb = &obex_tl_rfcomm_cb.scb[i]; + break; + } + } + return p_scb; +} + +static void free_ccb(tOBEX_TL_RFCOMM_CCB *p_ccb) +{ + memset(p_ccb, 0, sizeof(tOBEX_TL_RFCOMM_CCB)); +} + +static void free_scb(tOBEX_TL_RFCOMM_SCB *p_scb) +{ + memset(p_scb, 0, sizeof(tOBEX_TL_RFCOMM_SCB)); +} + +static tOBEX_TL_RFCOMM_CCB *find_ccb_by_handle(UINT16 handle) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = NULL; + if (handle > 0 && handle <= OBEX_TL_RFCOMM_NUM_CONN) { + if (obex_tl_rfcomm_cb.ccb[handle-1].allocated == handle) { + p_ccb = &obex_tl_rfcomm_cb.ccb[handle-1]; + } + } + return p_ccb; +} + +static tOBEX_TL_RFCOMM_CCB *find_ccb_by_rfc_handle(UINT16 rfc_handle) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = NULL; + for(int i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; ++i) { + if (obex_tl_rfcomm_cb.ccb[i].allocated && obex_tl_rfcomm_cb.ccb[i].rfc_handle == rfc_handle) { + p_ccb = &obex_tl_rfcomm_cb.ccb[i]; + break; + } + } + return p_ccb; +} + +static tOBEX_TL_RFCOMM_SCB *find_scb_by_handle(UINT16 handle) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = NULL; + handle = handle >> 8; + if (handle > 0 && handle <= OBEX_TL_RFCOMM_NUM_SERVER) { + if (obex_tl_rfcomm_cb.scb[handle-1].allocated == handle) { + p_scb = &obex_tl_rfcomm_cb.scb[handle-1]; + } + } + return p_scb; +} + +static tOBEX_TL_RFCOMM_SCB *find_scb_by_rfc_handle(UINT16 rfc_handle) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = NULL; + for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) { + if (obex_tl_rfcomm_cb.scb[i].allocated && obex_tl_rfcomm_cb.scb[i].rfc_handle == rfc_handle) { + p_scb = &obex_tl_rfcomm_cb.scb[i]; + break; + } + } + return p_scb; +} + +static tOBEX_TL_RFCOMM_SCB *find_scb_by_scn(UINT16 scn) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = NULL; + for(int i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; ++i) { + if (obex_tl_rfcomm_cb.scb[i].allocated && obex_tl_rfcomm_cb.scb[i].scn == scn) { + p_scb = &obex_tl_rfcomm_cb.scb[i]; + break; + } + } + return p_scb; +} + +static void rfcomm_mgmt_event_handler(tOBEX_TL_RFCOMM_CCB *p_ccb, UINT32 code) +{ + tOBEX_TL_MSG msg = {0}; + msg.any.hdl = p_ccb->allocated; + switch (code) + { + case PORT_SUCCESS: + /* event already handled, do nothing */ + break; + default: + /* other event, disconnect */ + obex_tl_rfcomm_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); + free_ccb(p_ccb); + break; + } +} + +static void rfcomm_client_mgmt_callback(UINT32 code, UINT16 rfc_handle, void* data) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle); + if (p_ccb == NULL) { + OBEX_TL_RFCOMM_TRACE_DEBUG("No ccb to handle rfcomm event\n"); + return; + } + /* connection opened, handle event here */ + if (code == PORT_SUCCESS) { + assert(data != NULL); + tPORT_MGMT_CL_CALLBACK_ARG *cl_mgmt_cb_arg = (tPORT_MGMT_CL_CALLBACK_ARG *)data; + p_ccb->mtu = cl_mgmt_cb_arg->peer_mtu; + + tOBEX_TL_MSG msg = {0}; + msg.conn_open.hdl = p_ccb->allocated; + msg.conn_open.peer_mtu = p_ccb->mtu; + msg.conn_open.our_mtu = p_ccb->mtu; + obex_tl_rfcomm_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg); + } + rfcomm_mgmt_event_handler(p_ccb, code); +} + +static void rfcomm_server_mgmt_callback(UINT32 code, UINT16 rfc_handle, void* data) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = NULL; + /* incoming connection, handle event here */ + if (code == PORT_SUCCESS) { + assert(data != NULL); + tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_rfc_handle(rfc_handle); + tPORT_MGMT_SR_CALLBACK_ARG *sr_mgmt_cb_arg = (tPORT_MGMT_SR_CALLBACK_ARG *)data; + if (p_scb == NULL) { + OBEX_TL_RFCOMM_TRACE_WARNING("No scb to this rfcomm connection\n"); + /* tell rfcomm to reject this connection */ + sr_mgmt_cb_arg->accept = FALSE; + return; + } + + /* try to find p_ccb with this rfc_handle, we expect to get a NULL */ + p_ccb = find_ccb_by_rfc_handle(rfc_handle); + if (p_ccb == NULL) { + p_ccb = allocate_ccb(); + if (p_ccb == NULL) { + OBEX_TL_RFCOMM_TRACE_WARNING("can not allocate a ccb for new connection\n"); + sr_mgmt_cb_arg->accept = FALSE; + return; + } + } + else { + OBEX_TL_RFCOMM_TRACE_WARNING("found duplicate rfcomm connection\n"); + if (sr_mgmt_cb_arg != NULL) { + sr_mgmt_cb_arg->accept = FALSE; + } + return; + } + + p_ccb->initiator = FALSE; + p_ccb->rfc_handle = rfc_handle; + p_ccb->scn = p_scb->scn; + p_ccb->mtu = sr_mgmt_cb_arg->peer_mtu; + /* get peer bd_addr */ + PORT_CheckConnection(rfc_handle, FALSE, p_ccb->addr, NULL); + + tOBEX_TL_MSG msg = {0}; + msg.conn_income.hdl = p_ccb->allocated; + msg.conn_income.peer_mtu = p_ccb->mtu; + msg.conn_income.our_mtu = p_ccb->mtu; + msg.conn_income.svr_hdl = (p_scb->allocated << 8); + obex_tl_rfcomm_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg); + } + else { + /* other event, it means server is connected */ + p_ccb = find_ccb_by_rfc_handle(rfc_handle); + if (p_ccb == NULL) { + OBEX_TL_RFCOMM_TRACE_DEBUG("No ccb to handle rfcomm event\n"); + return; + } + } + rfcomm_mgmt_event_handler(p_ccb, code); +} + +static int rfcomm_data_callback(UINT16 rfc_handle, UINT8 *p_buf, UINT16 len, int type) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle); + if (p_ccb != NULL && type == DATA_CO_CALLBACK_TYPE_INCOMING) { + tOBEX_TL_MSG msg = {0}; + msg.data.hdl = p_ccb->allocated; + msg.data.p_buf = (BT_HDR *)p_buf; + obex_tl_rfcomm_cb.callback(OBEX_TL_DATA_EVT, &msg); + PORT_FlowControl_GiveCredit(rfc_handle, TRUE, 1); + } + else if(p_buf != NULL) { + osi_free(p_buf); + } + return 1; +} + +static void rfcomm_event_callback(UINT32 code, UINT16 rfc_handle) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_rfc_handle(rfc_handle); + if (p_ccb == NULL) { + OBEX_TL_RFCOMM_TRACE_WARNING("No ccb to handle rfcomm event\n"); + return; + } + + if (code & PORT_EV_FC) { + tOBEX_TL_MSG msg = {0}; + msg.any.hdl = p_ccb->allocated; + if (code & PORT_EV_FCS) { + obex_tl_rfcomm_cb.callback(OBEX_TL_UNCONGEST_EVT, &msg); + } + else { + obex_tl_rfcomm_cb.callback(OBEX_TL_CONGEST_EVT, &msg); + } + } +} + +UINT16 obex_tl_rfcomm_init(tOBEX_TL_CBACK *callback) +{ + assert(callback != NULL); +#if (OBEX_DYNAMIC_MEMORY) + if (!obex_tl_rfcomm_cb_ptr) { + obex_tl_rfcomm_cb_ptr = (tOBEX_TL_RFCOMM_CB *)osi_malloc(sizeof(tOBEX_TL_RFCOMM_CB)); + if (!obex_tl_rfcomm_cb_ptr) { + OBEX_TL_RFCOMM_TRACE_ERROR("OBEX over RFCOMM transport layer initialize failed, no memory\n"); + return OBEX_TL_FAILED; + } + } +#endif /* #if (OBEX_DYNAMIC_MEMORY) */ + memset(&obex_tl_rfcomm_cb, 0, sizeof(tOBEX_TL_RFCOMM_CB)); + obex_tl_rfcomm_cb.callback = callback; + obex_tl_rfcomm_cb.trace_level = BT_TRACE_LEVEL_ERROR; + return OBEX_TL_SUCCESS; +} + +void obex_tl_rfcomm_deinit(void) +{ +#if (OBEX_DYNAMIC_MEMORY) + if (obex_tl_rfcomm_cb_ptr) { +#endif + for (size_t i = 0; i < OBEX_TL_RFCOMM_NUM_CONN; i++) { + if (obex_tl_rfcomm_cb.ccb[i].rfc_handle != 0) { + RFCOMM_RemoveConnection(obex_tl_rfcomm_cb.ccb[i].rfc_handle); + } + } + for (size_t i = 0; i < OBEX_TL_RFCOMM_NUM_SERVER; i++) { + if (obex_tl_rfcomm_cb.scb[i].rfc_handle != 0) { + RFCOMM_RemoveServer(obex_tl_rfcomm_cb.scb[i].rfc_handle); + } + } +#if (OBEX_DYNAMIC_MEMORY) + osi_free(obex_tl_rfcomm_cb_ptr); + obex_tl_rfcomm_cb_ptr = NULL; + } +#endif /* #if (OBEX_DYNAMIC_MEMORY) */ +} + +UINT16 obex_tl_rfcomm_connect(tOBEX_TL_SVR_INFO *server) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = allocate_ccb(); + if (p_ccb == NULL) { + return 0; + } + + BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->rfcomm.sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, server->rfcomm.scn); + if (RFCOMM_CreateConnection(UUID_PROTOCOL_OBEX, server->rfcomm.scn, FALSE, server->rfcomm.pref_mtu, + server->rfcomm.addr, &p_ccb->rfc_handle, rfcomm_client_mgmt_callback) != PORT_SUCCESS) { + free_ccb(p_ccb); + return 0; + } + + /* set up data callback, event mask and event callback */ + PORT_SetDataCOCallback(p_ccb->rfc_handle, rfcomm_data_callback); + PORT_SetEventMask(p_ccb->rfc_handle, OBEX_TL_RFCOMM_EVENT_MARK); + PORT_SetEventCallback(p_ccb->rfc_handle, rfcomm_event_callback); + + bdcpy(p_ccb->addr, server->rfcomm.addr); + p_ccb->scn = server->rfcomm.scn; + p_ccb->initiator = TRUE; + + return p_ccb->allocated; +} + +void obex_tl_rfcomm_disconnect(UINT16 handle) +{ + tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_handle(handle); + if (p_ccb != NULL) { + RFCOMM_RemoveConnection(p_ccb->rfc_handle); + free_ccb(p_ccb); + } +} + +UINT16 obex_tl_rfcomm_send(UINT16 handle, BT_HDR *p_buf) +{ + UINT16 ret = OBEX_TL_FAILED; + tOBEX_TL_RFCOMM_CCB *p_ccb = find_ccb_by_handle(handle); + do { + if (p_ccb == NULL) { + osi_free(p_buf); + break; + } + + /* Can not send data size larger than MTU */ + /* Offset should not smaller than OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET */ + if (p_buf->len > p_ccb->mtu || p_buf->offset < OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET) { + osi_free(p_buf); + break; + } + + if (PORT_Write(p_ccb->rfc_handle, p_buf) == PORT_SUCCESS) { + ret = OBEX_TL_SUCCESS; + } + } while (0); + return ret; +} + +UINT16 obex_tl_rfcomm_bind(tOBEX_TL_SVR_INFO *server) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_scn(server->rfcomm.scn); + if (p_scb != NULL) { + /* scn already used */ + return 0; + } + + p_scb = allocate_scb(); + if (p_scb == NULL) { + OBEX_TL_RFCOMM_TRACE_WARNING("Can not allocate scb, out of number\n"); + return 0; + } + + BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_OBEX, server->rfcomm.sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, server->rfcomm.scn); + if (RFCOMM_CreateConnection(UUID_PROTOCOL_OBEX, server->rfcomm.scn, TRUE, server->rfcomm.pref_mtu, + server->rfcomm.addr, &p_scb->rfc_handle, rfcomm_server_mgmt_callback) != PORT_SUCCESS) { + free_scb(p_scb); + return 0; + } + + /* set up data callback, event mask and event callback */ + PORT_SetDataCOCallback(p_scb->rfc_handle, rfcomm_data_callback); + PORT_SetEventMask(p_scb->rfc_handle, OBEX_TL_RFCOMM_EVENT_MARK); + PORT_SetEventCallback(p_scb->rfc_handle, rfcomm_event_callback); + + p_scb->scn = server->rfcomm.scn; + + /* left shift 8 bits as server handle, avoid confuse with connection handle */ + return (p_scb->allocated << 8); +} + +void obex_tl_rfcomm_unbind(UINT16 handle) +{ + tOBEX_TL_RFCOMM_SCB *p_scb = find_scb_by_handle(handle); + if (p_scb) { + tOBEX_TL_RFCOMM_CCB *p_ccb = NULL; + while ((p_ccb = find_ccb_by_rfc_handle(p_scb->rfc_handle)) != NULL) { + RFCOMM_RemoveConnection(p_ccb->rfc_handle); + tOBEX_TL_MSG msg = {0}; + msg.any.hdl = p_ccb->allocated; + obex_tl_rfcomm_cb.callback(OBEX_TL_DIS_CONN_EVT, &msg); + free_ccb(p_ccb); + } + RFCOMM_RemoveServer(p_scb->rfc_handle); + free_scb(p_scb); + } +} + +static tOBEX_TL_OPS obex_tl_rfcomm_ops = { + .init = obex_tl_rfcomm_init, + .deinit = obex_tl_rfcomm_deinit, + .connect = obex_tl_rfcomm_connect, + .disconnect = obex_tl_rfcomm_disconnect, + .bind = obex_tl_rfcomm_bind, + .unbind = obex_tl_rfcomm_unbind, + .send = obex_tl_rfcomm_send +}; + +/******************************************************************************* +** +** Function obex_tl_rfcomm_ops_get +** +** Description Get the operation function structure pointer of OBEX over +** RFCOMM transport layer +** +** Returns Pointer to operation function structure +** +*******************************************************************************/ +tOBEX_TL_OPS *obex_tl_rfcomm_ops_get(void) +{ + return &obex_tl_rfcomm_ops; +} + +#endif /* #if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) */