diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 093592b870..6c90ad2a01 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -411,6 +411,7 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/stack/obex/obex_api.c" "host/bluedroid/stack/obex/obex_main.c" "host/bluedroid/stack/obex/obex_tl_l2cap.c" + "host/bluedroid/stack/obex/obex_tl_rfcomm.c" "host/bluedroid/stack/rfcomm/port_api.c" "host/bluedroid/stack/rfcomm/port_rfc.c" "host/bluedroid/stack/rfcomm/port_utils.c" 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 0e869dad45..58e15435bf 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -177,6 +177,9 @@ #endif /* UC_BT_HID_DEVICE_ENABLED */ #if UC_BT_GOEPC_ENABLED +#ifndef RFCOMM_INCLUDED +#define RFCOMM_INCLUDED TRUE +#endif #ifndef OBEX_INCLUDED #define OBEX_INCLUDED TRUE #endif diff --git a/components/bt/host/bluedroid/common/include/common/bt_trace.h b/components/bt/host/bluedroid/common/include/common/bt_trace.h index 74b7f10574..9acaf69097 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_trace.h +++ b/components/bt/host/bluedroid/common/include/common/bt_trace.h @@ -330,6 +330,13 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define OBEX_TL_L2CAP_TRACE_EVENT(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("OBEX_TL_L2CAP", fmt, ## args);} #define OBEX_TL_L2CAP_TRACE_DEBUG(fmt, args...) {if (obex_tl_l2cap_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("OBEX_TL_L2CAP", fmt, ## args);} +/* Define tracing for OBEX_TL_RFCOMM */ +#define OBEX_TL_RFCOMM_TRACE_ERROR(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("OBEX_TL_RFCOMM", fmt, ## args);} +#define OBEX_TL_RFCOMM_TRACE_WARNING(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("OBEX_TL_RFCOMM", fmt, ## args);} +#define OBEX_TL_RFCOMM_TRACE_API(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(AVRC,API)) BT_PRINT_I("OBEX_TL_RFCOMM", fmt, ## args);} +#define OBEX_TL_RFCOMM_TRACE_EVENT(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(AVRC,EVENT)) BT_PRINT_D("OBEX_TL_RFCOMM", fmt, ## args);} +#define OBEX_TL_RFCOMM_TRACE_DEBUG(fmt, args...) {if (obex_tl_rfcomm_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(AVRC,DEBUG)) BT_PRINT_D("OBEX_TL_RFCOMM", fmt, ## args);} + /* Define tracing for GOEPC */ #define GOEPC_TRACE_ERROR(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(AVRC, ERROR)) BT_PRINT_E("BT_GOEPC", fmt, ## args);} #define GOEPC_TRACE_WARNING(fmt, args...) {if (goepc_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(AVRC, WARNING)) BT_PRINT_W("BT_GOEPC", fmt, ## args);} @@ -512,12 +519,20 @@ extern UINT8 btif_trace_level; #define OBEX_TRACE_EVENT(fmt, args...) #define OBEX_TRACE_DEBUG(fmt, args...) +/* Define tracing for OBEX L2CAP transport layer */ #define OBEX_TL_L2CAP_TRACE_ERROR(fmt, args...) #define OBEX_TL_L2CAP_TRACE_WARNING(fmt, args...) #define OBEX_TL_L2CAP_TRACE_API(fmt, args...) #define OBEX_TL_L2CAP_TRACE_EVENT(fmt, args...) #define OBEX_TL_L2CAP_TRACE_DEBUG(fmt, args...) +/* Define tracing for OBEX RFCOMM transport layer */ +#define OBEX_TL_RFCOMM_TRACE_ERROR(fmt, args...) +#define OBEX_TL_RFCOMM_TRACE_WARNING(fmt, args...) +#define OBEX_TL_RFCOMM_TRACE_API(fmt, args...) +#define OBEX_TL_RFCOMM_TRACE_EVENT(fmt, args...) +#define OBEX_TL_RFCOMM_TRACE_DEBUG(fmt, args...) + /* Define tracing for GOEPC */ #define GOEPC_TRACE_ERROR(fmt, args...) #define GOEPC_TRACE_WARNING(fmt, args...) diff --git a/components/bt/host/bluedroid/stack/include/stack/obex_api.h b/components/bt/host/bluedroid/stack/include/stack/obex_api.h index e130a48734..d22cd33c52 100644 --- a/components/bt/host/bluedroid/stack/include/stack/obex_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/obex_api.h @@ -19,8 +19,6 @@ #define OBEX_NOT_OPEN 6 /* Connection not open */ #define OBEX_PACKET_TOO_LARGE 7 /* Packet size large than MTU */ #define OBEX_ERROR_TL 8 /* Operation failed in transport layer */ -#define OBEX_TRY_AGAIN 9 /* Operation failed, connection congestion, try again */ - /* * OBEX profile definitions @@ -162,12 +160,21 @@ typedef struct BD_ADDR addr; /* peer bluetooth device address */ } tOBEX_OVER_L2CAP_SVR; +typedef struct +{ + UINT8 scn; /* service channel number */ + UINT16 sec_mask; /* security mask */ + UINT16 pref_mtu; /* preferred mtu, limited by rfcomm mtu */ + BD_ADDR addr; /* peer bluetooth device address */ +} tOBEX_OVER_RFCOMM_SVR; + typedef struct { UINT8 tl; /* transport type, OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */ union { tOBEX_OVER_L2CAP_SVR l2cap; + tOBEX_OVER_RFCOMM_SVR rfcomm; }; } tOBEX_SVR_INFO; diff --git a/components/bt/host/bluedroid/stack/obex/include/obex_int.h b/components/bt/host/bluedroid/stack/obex/include/obex_int.h index 4512e89c2a..b6455baa35 100644 --- a/components/bt/host/bluedroid/stack/obex/include/obex_int.h +++ b/components/bt/host/bluedroid/stack/obex/include/obex_int.h @@ -14,7 +14,13 @@ #if (OBEX_INCLUDED == TRUE) -#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET /* should set to max value of all transport layer */ +#if (RFCOMM_INCLUDED == TRUE) +#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET /* should set to max value of all transport layer */ +#define OBEX_BT_HDR_RESERVE_LEN OBEX_TL_RFCOMM_BT_HDR_RESERVE_LEN /* should set to max value of all transport layer */ +#else +#define OBEX_BT_HDR_MIN_OFFSET OBEX_TL_L2CAP_BT_HDR_OFFSET_MIN +#define OBEX_BT_HDR_RESERVE_LEN OBEX_TL_L2CAP_BT_HDR_RESERVE_LEN +#endif #define OBEX_ROLE_CLIENT 0x01 #define OBEX_ROLE_SERVER 0x02 @@ -65,6 +71,7 @@ extern tOBEX_CB *obex_cb_ptr; #endif void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg); +void obex_tl_rfcomm_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg); tOBEX_CCB *obex_allocate_ccb(void); tOBEX_SCB *obex_allocate_scb(void); void obex_free_ccb(tOBEX_CCB *p_ccb); diff --git a/components/bt/host/bluedroid/stack/obex/include/obex_tl.h b/components/bt/host/bluedroid/stack/obex/include/obex_tl.h index 6a60ed51dc..9211b9b0c2 100644 --- a/components/bt/host/bluedroid/stack/obex/include/obex_tl.h +++ b/components/bt/host/bluedroid/stack/obex/include/obex_tl.h @@ -68,9 +68,18 @@ typedef struct BD_ADDR addr; /* peer bluetooth device address */ } tOBEX_TL_L2CAP_SVR; +typedef struct +{ + UINT8 scn; /* service channel number */ + UINT16 sec_mask; /* security mask */ + UINT16 pref_mtu; /* preferred mtu, limited by rfcomm mtu */ + BD_ADDR addr; /* peer bluetooth device address */ +} tOBEX_TL_RFCOMM_SVR; + typedef union { tOBEX_TL_L2CAP_SVR l2cap; + tOBEX_TL_RFCOMM_SVR rfcomm; } tOBEX_TL_SVR_INFO; typedef void (tOBEX_TL_CBACK)(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg); diff --git a/components/bt/host/bluedroid/stack/obex/include/obex_tl_l2cap.h b/components/bt/host/bluedroid/stack/obex/include/obex_tl_l2cap.h index 83f062bb69..326677665a 100644 --- a/components/bt/host/bluedroid/stack/obex/include/obex_tl_l2cap.h +++ b/components/bt/host/bluedroid/stack/obex/include/obex_tl_l2cap.h @@ -10,7 +10,8 @@ #if (OBEX_INCLUDED == TRUE) -#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* L2CAP_MIN_OFFSET */ +#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* equal to L2CAP_MIN_OFFSET */ +#define OBEX_TL_L2CAP_BT_HDR_RESERVE_LEN 0 /* not require any additional byte */ tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void); diff --git a/components/bt/host/bluedroid/stack/obex/include/obex_tl_rfcomm.h b/components/bt/host/bluedroid/stack/obex/include/obex_tl_rfcomm.h new file mode 100644 index 0000000000..61b9d29589 --- /dev/null +++ b/components/bt/host/bluedroid/stack/obex/include/obex_tl_rfcomm.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "obex_tl.h" + +#if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) + +#define OBEX_TL_RFCOMM_BT_HDR_MIN_OFFSET 18 /* RFCOMM_MIN_OFFSET + L2CAP_MIN_OFFSET */ +#define OBEX_TL_RFCOMM_BT_HDR_RESERVE_LEN 1 /* reserve 1 byte for rfcomm fcs */ + +tOBEX_TL_OPS *obex_tl_rfcomm_ops_get(void); + +#endif /* #if (OBEX_INCLUDED == TRUE && RFCOMM_INCLUDED == TRUE) */ diff --git a/components/bt/host/bluedroid/stack/obex/obex_api.c b/components/bt/host/bluedroid/stack/obex/obex_api.c index befabb0e92..a3487f3e5a 100644 --- a/components/bt/host/bluedroid/stack/obex/obex_api.c +++ b/components/bt/host/bluedroid/stack/obex/obex_api.c @@ -14,6 +14,7 @@ #include "obex_int.h" #include "obex_tl.h" #include "obex_tl_l2cap.h" +#include "obex_tl_rfcomm.h" #if (OBEX_INCLUDED == TRUE) @@ -25,6 +26,12 @@ static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR tl_server->l2cap.pref_mtu = server->l2cap.pref_mtu; bdcpy(tl_server->l2cap.addr, server->l2cap.addr); } + else if (server->tl == OBEX_OVER_RFCOMM) { + tl_server->rfcomm.scn = server->rfcomm.scn; + tl_server->rfcomm.sec_mask = server->rfcomm.sec_mask; + tl_server->rfcomm.pref_mtu = server->rfcomm.pref_mtu; + bdcpy(tl_server->rfcomm.addr, server->rfcomm.addr); + } else { OBEX_TRACE_ERROR("Unsupported OBEX transport type\n"); assert(0); @@ -62,13 +69,12 @@ UINT16 OBEX_Init(void) if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->init != NULL) { obex_cb.tl_ops[OBEX_OVER_L2CAP]->init(obex_tl_l2cap_callback); } - /* Not implement yet */ - /* +#if (RFCOMM_INCLUDED == TRUE) obex_cb.tl_ops[OBEX_OVER_RFCOMM] = obex_tl_rfcomm_ops_get(); if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init != NULL) { obex_cb.tl_ops[OBEX_OVER_RFCOMM]->init(obex_tl_rfcomm_callback); } - */ +#endif obex_cb.trace_level = BT_TRACE_LEVEL_ERROR; return OBEX_SUCCESS; } @@ -317,7 +323,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_ if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) { return OBEX_INVALID_PARAM; } - buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET; + buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN; BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size); if (p_buf == NULL) { @@ -327,7 +333,7 @@ UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_ UINT16 pkt_len = OBEX_MIN_PACKET_SIZE; p_buf->offset = OBEX_BT_HDR_MIN_OFFSET; /* use layer_specific to store the max data length allowed */ - p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET; + p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN; UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; /* byte 0: opcode */ *p_data++ = info->opcode; @@ -378,7 +384,7 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out if (buff_size < OBEX_MIN_PACKET_SIZE || info == NULL || out_pkt == NULL) { return OBEX_INVALID_PARAM; } - buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET; + buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET + OBEX_BT_HDR_RESERVE_LEN; BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size); if (p_buf == NULL) { @@ -388,7 +394,7 @@ UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out UINT16 pkt_len = OBEX_MIN_PACKET_SIZE; p_buf->offset = OBEX_BT_HDR_MIN_OFFSET; /* use layer_specific to store the max data length allowed */ - p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET; + p_buf->layer_specific = buff_size - sizeof(BT_HDR) - OBEX_BT_HDR_MIN_OFFSET - OBEX_BT_HDR_RESERVE_LEN; UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset; /* byte 0: response code */ *p_data++ = info->response_code; diff --git a/components/bt/host/bluedroid/stack/obex/obex_main.c b/components/bt/host/bluedroid/stack/obex/obex_main.c index c5f0453902..7810ff3c33 100644 --- a/components/bt/host/bluedroid/stack/obex/obex_main.c +++ b/components/bt/host/bluedroid/stack/obex/obex_main.c @@ -208,4 +208,9 @@ void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) obex_tl_evt_handler(OBEX_OVER_L2CAP, evt, msg); } +void obex_tl_rfcomm_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg) +{ + obex_tl_evt_handler(OBEX_OVER_RFCOMM, evt, msg); +} + #endif /* #if (OBEX_INCLUDED == TRUE) */ 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 0000000000..ce7799c867 --- /dev/null +++ b/components/bt/host/bluedroid/stack/obex/obex_tl_rfcomm.c @@ -0,0 +1,439 @@ +/* + * 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/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"); + } + + 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); + } + } +} + +void 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"); + assert(0); + } + } +#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; +} + +void obex_tl_rfcomm_deinit(void) +{ +#if (OBEX_DYNAMIC_MEMORY) + if (obex_tl_rfcomm_cb_ptr) { + 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) */