mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(bt/bluedroid): Support OBEX over L2CAP
This commit is contained in:
parent
928addee19
commit
da858edb7a
@ -1809,6 +1809,15 @@
|
||||
#define OBX_FCR_TX_POOL_ID 3
|
||||
#endif
|
||||
|
||||
/* Maximum OBEX connection allowed */
|
||||
#ifndef OBEX_MAX_CONNECTION
|
||||
#define OBEX_MAX_CONNECTION 3
|
||||
#endif
|
||||
|
||||
/* Maximum OBEX server allowed */
|
||||
#ifndef OBEX_MAX_SERVER
|
||||
#define OBEX_MAX_SERVER 2
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
**
|
||||
|
264
components/bt/host/bluedroid/stack/include/stack/obex_api.h
Normal file
264
components/bt/host/bluedroid/stack/include/stack/obex_api.h
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
/* API function return value result codes. */
|
||||
#define OBEX_SUCCESS 0 /* Operation successful */
|
||||
#define OBEX_FAILURE 1 /* Operation failed */
|
||||
#define OBEX_NO_RESOURCES 3 /* Not enough resources */
|
||||
#define OBEX_BAD_HANDLE 4 /* Bad handle */
|
||||
#define OBEX_INVALID_PARAM 5 /* Invalid parameter */
|
||||
#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
|
||||
*/
|
||||
#define OBEX_OPCODE_CONNECT 0x80
|
||||
#define OBEX_OPCODE_DISCONNECT 0x81
|
||||
#define OBEX_OPCODE_PUT 0x02
|
||||
#define OBEX_OPCODE_PUT_FINAL 0x82
|
||||
#define OBEX_OPCODE_GET 0x03
|
||||
#define OBEX_OPCODE_GET_FINAL 0x83
|
||||
#define OBEX_OPCODE_SETPATH 0x85
|
||||
#define OBEX_OPCODE_ACTION 0x06
|
||||
#define OBEX_OPCODE_SESSION 0x87
|
||||
#define OBEX_OPCODE_ABORT 0xFF
|
||||
|
||||
#define OBEX_RESPONSE_CODE_CONTINUE 0x10
|
||||
#define OBEX_RESPONSE_CODE_OK 0x20
|
||||
#define OBEX_RESPONSE_CODE_CREATED 0x21
|
||||
#define OBEX_RESPONSE_CODE_ACCEPTED 0x22
|
||||
#define OBEX_RESPONSE_CODE_NON_AUTHORITATIVE_INFO 0x23
|
||||
#define OBEX_RESPONSE_CODE_NO_CONTENT 0x24
|
||||
#define OBEX_RESPONSE_CODE_RESET_CONTENT 0x25
|
||||
#define OBEX_RESPONSE_CODE_PARTIAL_CONTENT 0x26
|
||||
#define OBEX_RESPONSE_CODE_MULTIPLE_CHOICES 0x30
|
||||
#define OBEX_RESPONSE_CODE_MOVED_PERMANENTLY 0x31
|
||||
#define OBEX_RESPONSE_CODE_MOVED_TEMPORARILY 0x31
|
||||
#define OBEX_RESPONSE_CODE_SEE_OHTER 0x33
|
||||
#define OBEX_RESPONSE_CODE_NOT_MODIFIED 0x34
|
||||
#define OBEX_RESPONSE_CODE_USE_PROXY 0x35
|
||||
#define OBEX_RESPONSE_CODE_BAD_REQUEST 0x40
|
||||
#define OBEX_RESPONSE_CODE_UNAUTHORIZED 0x41
|
||||
#define OBEX_RESPONSE_CODE_PAYMENT_REQUIRED 0x42
|
||||
#define OBEX_RESPONSE_CODE_FORBIDDEN 0x43
|
||||
#define OBEX_RESPONSE_CODE_NOT_FOUND 0x44
|
||||
#define OBEX_RESPONSE_CODE_METHOD_NOT_ALLOWED 0x45
|
||||
#define OBEX_RESPONSE_CODE_NOT_ACCEPTABLE 0x46
|
||||
#define OBEX_RESPONSE_CODE_PROXY_AUTHENTICATION_REQUIRED 0x47
|
||||
#define OBEX_RESPONSE_CODE_REQUEST_TIME_OUT 0x48
|
||||
#define OBEX_RESPONSE_CODE_CONFLICT 0x49
|
||||
#define OBEX_RESPONSE_CODE_GONE 0x4A
|
||||
#define OBEX_RESPONSE_CODE_LENGTH_REQUIRED 0x4B
|
||||
#define OBEX_RESPONSE_CODE_PRECONDITION_FAILED 0x4C
|
||||
#define OBEX_RESPONSE_CODE_REQUESTED_ENTITY_TOO_LARGE 0x4D
|
||||
#define OBEX_RESPONSE_CODE_REQUEST_URL_TOO_LARGE 0x4E
|
||||
#define OBEX_RESPONSE_CODE_UNSUPPORTED_MEDIA_TYPE 0x4F
|
||||
#define OBEX_RESPONSE_CODE_INTERNAL_SERVER_ERROR 0x50
|
||||
#define OBEX_RESPONSE_CODE_NOT_IMPLEMENTED 0x51
|
||||
#define OBEX_RESPONSE_CODE_BAD_GATEWAY 0x52
|
||||
#define OBEX_RESPONSE_CODE_SERVICE_UNAVAILABLE 0x53
|
||||
#define OBEX_RESPONSE_CODE_GATEWAY_TIMEOUT 0x54
|
||||
#define OBEX_RESPONSE_CODE_HTTP_VERSION_NOT_SUPPORTED 0x55
|
||||
#define OBEX_RESPONSE_CODE_DATABASE_FULL 0x60
|
||||
#define OBEX_RESPONSE_CODE_DATABASE_LOCKED 0x61
|
||||
|
||||
#define OBEX_FINAL_BIT_MASK 0x80
|
||||
|
||||
#define OBEX_CONNECT_FLAGS 0x01 /* support multiple link */
|
||||
#define OBEX_SETPATH_FLAGS 0x03 /* default flags */
|
||||
|
||||
#define OBEX_PACKET_LENGTH_MAX (0xFFFF-1)
|
||||
#define OBEX_PACKET_LENGTH_MIN 255
|
||||
|
||||
#define OBEX_VERSION_NUMBER 0x15
|
||||
|
||||
/* Header identifiers */
|
||||
#define OBEX_HEADER_ID_U2B_MASK 0xC0 /* upper 2 bits of header ID are user to indicate the header encoding */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE1 0x00 /* null terminated Unicode text, length prefixed with 2 byte unsigned integer */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE2 0x40 /* byte sequence, length prefixed with 2 byte unsigned integer */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE3 0x80 /* 1 byte quantity */
|
||||
#define OBEX_HEADER_ID_U2B_TYPE4 0xC0 /* 4 byte quantity - transmitted in network byte order (high byte first) */
|
||||
|
||||
#define OBEX_HEADER_ID_COUNT 0xC0
|
||||
#define OBEX_HEADER_ID_NAME 0x01
|
||||
#define OBEX_HEADER_ID_TYPE 0x42
|
||||
#define OBEX_HEADER_ID_LENGTH 0xC3
|
||||
#define OBEX_HEADER_ID_TIME_ISO8601 0x44
|
||||
#define OBEX_HEADER_ID_TIME_4BYTE 0xC4
|
||||
#define OBEX_HEADER_ID_DESCRIPTION 0x05
|
||||
#define OBEX_HEADER_ID_TARGET 0x46
|
||||
#define OBEX_HEADER_ID_HTTP 0x47
|
||||
#define OBEX_HEADER_ID_BODY 0x48
|
||||
#define OBEX_HEADER_ID_END_OF_BODY 0x49
|
||||
#define OBEX_HEADER_ID_WHO 0x4A
|
||||
#define OBEX_HEADER_ID_CONNECTION_ID 0xCB
|
||||
#define OBEX_HEADER_ID_APP_PARAM 0x4C
|
||||
#define OBEX_HEADER_ID_AUTH_CHALLENGE 0x4D
|
||||
#define OBEX_HEADER_ID_AUTH_RESPONSE 0x4E
|
||||
#define OBEX_HEADER_ID_CREATOR_ID 0xCF
|
||||
#define OBEX_HEADER_ID_WAN_UUID 0x50
|
||||
#define OBEX_HEADER_ID_OBJECT_CLASS 0x51
|
||||
#define OBEX_HEADER_ID_SESSION_PARAM 0x52
|
||||
#define OBEX_HEADER_ID_SESSION_SEQ_NUM 0x93
|
||||
#define OBEX_HEADER_ID_ACTION_ID 0x94
|
||||
#define OBEX_HEADER_ID_DESTNAME 0x15
|
||||
#define OBEX_HEADER_ID_PERMISSIONS 0xD6
|
||||
#define OBEX_HEADER_ID_SRM 0x97
|
||||
#define OBEX_HEADER_ID_SRM_PARAM 0x98
|
||||
/* Reserved for future use: 0x19 to 0x2F */
|
||||
/* User defined: 0x30 to 0x3F */
|
||||
|
||||
#define OBEX_ACTION_ID_COPY 0x00
|
||||
#define OBEX_ACTION_ID_MOVE_RENAME 0x01
|
||||
#define OBEX_ACTION_ID_SET_PERMISSIONS 0x02
|
||||
|
||||
#define OBEX_SRM_DISABLE 0x00
|
||||
#define OBEX_SRM_ENABLE 0x01
|
||||
#define OBEX_SRM_SUPPORT 0x02
|
||||
|
||||
#define OBEX_SRMP_ADD_PKT 0x00
|
||||
#define OBEX_SRMP_WAIT 0x01
|
||||
#define OBEX_SRMP_ADD_PKT_WAIT 0x02
|
||||
|
||||
#define OBEX_MIN_PACKET_SIZE 3
|
||||
|
||||
enum {
|
||||
/* client event */
|
||||
OBEX_CONNECT_EVT, /* connection opened */
|
||||
OBEX_DISCONNECT_EVT, /* connection disconnected */
|
||||
/* server event */
|
||||
OBEX_CONN_INCOME_EVT, /* an incoming connection */
|
||||
/* client or server event */
|
||||
OBEX_MTU_CHANGE_EVT, /* connection mtu changed */
|
||||
OBEX_CONGEST_EVT, /* connection congested */
|
||||
OBEX_UNCONGEST_EVT, /* connection is not congested */
|
||||
OBEX_DATA_EVT /* data received */
|
||||
};
|
||||
|
||||
enum {
|
||||
OBEX_OVER_L2CAP = 0,
|
||||
OBEX_OVER_RFCOMM,
|
||||
OBEX_NUM_TL
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 psm; /* l2cap psm */
|
||||
UINT16 sec_mask; /* security mask */
|
||||
UINT16 pref_mtu; /* preferred mtu, limited by L2CAP_MTU_SIZE */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_OVER_L2CAP_SVR;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT8 tl; /* transport type, OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
union
|
||||
{
|
||||
tOBEX_OVER_L2CAP_SVR l2cap;
|
||||
};
|
||||
} tOBEX_SVR_INFO;
|
||||
|
||||
typedef struct {
|
||||
UINT8 opcode;
|
||||
UINT8 response_code;
|
||||
/* Connect */
|
||||
UINT8 obex_version_number;
|
||||
UINT16 max_packet_length;
|
||||
/* Connect or SetPath */
|
||||
UINT8 flags;
|
||||
/* Internal use */
|
||||
UINT16 next_header_pos;
|
||||
} tOBEX_PARSE_INFO;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} connect;
|
||||
|
||||
struct {
|
||||
UINT16 svr_handle;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} conn_income;
|
||||
|
||||
struct {
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} mtu_change;
|
||||
|
||||
struct {
|
||||
BT_HDR *pkt;
|
||||
} data;
|
||||
} tOBEX_MSG;
|
||||
|
||||
typedef void (tOBEX_MSG_CBACK)(UINT16 handle, UINT8 event, tOBEX_MSG *msg);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs are called by bluetooth stack automatically
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_Init(void);
|
||||
|
||||
extern void OBEX_Deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs must be executed in btu task
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_handle);
|
||||
|
||||
extern UINT16 OBEX_RemoveConn(UINT16 handle);
|
||||
|
||||
extern UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_svr_handle);
|
||||
|
||||
extern UINT16 OBEX_DeregisterServer(UINT16 svr_handle);
|
||||
|
||||
/*******************************************************************************
|
||||
* The following APIs are util function, can be executed in btu or btc task
|
||||
*******************************************************************************/
|
||||
|
||||
extern UINT16 OBEX_BuildRequest(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt);
|
||||
|
||||
extern UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt);
|
||||
|
||||
extern UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderSRM(BT_HDR *pkt, UINT8 value);
|
||||
|
||||
extern UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value);
|
||||
|
||||
extern UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_GetPacketLength(BT_HDR *pkt);
|
||||
|
||||
extern UINT16 OBEX_ParseRequest(BT_HDR *pkt, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern UINT16 OBEX_ParseResponse(BT_HDR *pkt, UINT8 opcode, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern BOOLEAN OBEX_CheckFinalBit(BT_HDR *pkt);
|
||||
|
||||
extern BOOLEAN OBEX_CheckContinueResponse(BT_HDR *pkt);
|
||||
|
||||
extern UINT8 *OBEX_GetNextHeader(BT_HDR *pkt, tOBEX_PARSE_INFO *info);
|
||||
|
||||
extern UINT16 OBEX_GetHeaderLength(UINT8 *header);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
73
components/bt/host/bluedroid/stack/obex/include/obex_int.h
Normal file
73
components/bt/host/bluedroid/stack/obex/include/obex_int.h
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#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 */
|
||||
|
||||
#define OBEX_ROLE_CLIENT 0x01
|
||||
#define OBEX_ROLE_SERVER 0x02
|
||||
|
||||
/* OBEX connection state */
|
||||
#define OBEX_STATE_IDLE 0 /* No connection */
|
||||
#define OBEX_STATE_OPENING 1 /* Starting to open a connection */
|
||||
#define OBEX_STATE_OPENED 2 /* Connection opened */
|
||||
|
||||
/* Store 16 bits data in big endian format, not modify the p_buf */
|
||||
#define STORE16BE(p_buf, data) do { *p_buf = ((data)>>8)&0xff; \
|
||||
*(p_buf+1) = (data)&0xff;} while(0)
|
||||
|
||||
/* OBEX Connection Control block */
|
||||
typedef struct {
|
||||
tOBEX_MSG_CBACK *callback; /* Connection msg callback function */
|
||||
UINT16 tl_hdl; /* Transport layer non-zeros connection handle*/
|
||||
UINT16 tl_peer_mtu; /* Transport layer peer mtu */
|
||||
UINT16 tl_our_mtu; /* Transport layer our mtu */
|
||||
UINT8 tl_cong; /* 1 if transport layer congestion, otherwise 0 */
|
||||
UINT8 tl; /* OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
UINT8 allocated; /* 0, not allocated. index+1, otherwise. equal to api handle */
|
||||
UINT8 state; /* This OBEX connection state */
|
||||
UINT8 role; /* This OBEX connection role */
|
||||
} tOBEX_CCB;
|
||||
|
||||
/* OBEX Server Control block */
|
||||
typedef struct {
|
||||
tOBEX_MSG_CBACK *callback; /* Connection msg callback function */
|
||||
UINT16 tl_hdl; /* Transport layer non-zeros server handle*/
|
||||
UINT8 tl; /* OBEX_OVER_L2CAP or OBEX_OVER_RFCOMM */
|
||||
UINT8 allocated; /* 0, not allocated. index+1, otherwise. */
|
||||
} tOBEX_SCB;
|
||||
|
||||
/* OBEX Control block */
|
||||
typedef struct {
|
||||
tOBEX_CCB ccb[OBEX_MAX_CONNECTION]; /* connection control blocks */
|
||||
tOBEX_SCB scb[OBEX_MAX_SERVER]; /* server control blocks */
|
||||
tOBEX_TL_OPS *tl_ops[OBEX_NUM_TL]; /* transport operation function pointer */
|
||||
UINT8 trace_level; /* trace level */
|
||||
} tOBEX_CB;
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
extern tOBEX_CB obex_cb;
|
||||
#else
|
||||
extern tOBEX_CB *obex_cb_ptr;
|
||||
#define obex_cb (*obex_cb_ptr)
|
||||
#endif
|
||||
|
||||
void obex_tl_l2cap_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);
|
||||
void obex_free_scb(tOBEX_SCB *p_scb);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
88
components/bt/host/bluedroid/stack/obex/include/obex_tl.h
Normal file
88
components/bt/host/bluedroid/stack/obex/include/obex_tl.h
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/bt_target.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
/* Return code of obex_tl_send_data */
|
||||
#define OBEX_TL_FAILED FALSE
|
||||
#define OBEX_TL_SUCCESS TRUE
|
||||
#define OBEX_TL_CONGESTED 2
|
||||
|
||||
typedef enum {
|
||||
OBEX_TL_CONN_OPEN_EVT,
|
||||
OBEX_TL_CONN_INCOME_EVT,
|
||||
OBEX_TL_DIS_CONN_EVT,
|
||||
OBEX_TL_CONGEST_EVT,
|
||||
OBEX_TL_UNCONGEST_EVT,
|
||||
OBEX_TL_MTU_CHANGE_EVT,
|
||||
OBEX_TL_DATA_EVT
|
||||
} tOBEX_TL_EVT;
|
||||
|
||||
typedef union {
|
||||
/* general struct, used to retrieve handle */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
} any;
|
||||
|
||||
/* struct for OBEX_TL_CONN_OPEN_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} conn_open;
|
||||
|
||||
/* struct for OBEX_TL_CONN_INCOME_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
UINT16 svr_hdl;
|
||||
} conn_income;
|
||||
|
||||
/* struct for OBEX_TL_MTU_CHANGE_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
UINT16 peer_mtu;
|
||||
UINT16 our_mtu;
|
||||
} mtu_chg;
|
||||
|
||||
/* struct for OBEX_TL_DATA_EVT */
|
||||
struct {
|
||||
UINT16 hdl;
|
||||
BT_HDR *p_buf;
|
||||
} data;
|
||||
} tOBEX_TL_MSG;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 psm; /* l2cap psm */
|
||||
UINT16 sec_mask; /* security mask */
|
||||
UINT16 pref_mtu; /* preferred mtu, limited by L2CAP_MTU_SIZE */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_TL_L2CAP_SVR;
|
||||
|
||||
typedef union
|
||||
{
|
||||
tOBEX_TL_L2CAP_SVR l2cap;
|
||||
} tOBEX_TL_SVR_INFO;
|
||||
|
||||
typedef void (tOBEX_TL_CBACK)(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg);
|
||||
|
||||
typedef struct {
|
||||
void (*init)(tOBEX_TL_CBACK *callback);
|
||||
void (*deinit)(void);
|
||||
UINT16 (*connect)(tOBEX_TL_SVR_INFO *server);
|
||||
void (*disconnect)(UINT16 tl_hdl);
|
||||
UINT16 (*send)(UINT16 tl_hdl, BT_HDR *p_buf);
|
||||
UINT16 (*bind)(tOBEX_TL_SVR_INFO *server);
|
||||
void (*unbind)(UINT16 tl_hdl);
|
||||
} tOBEX_TL_OPS;
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "obex_tl.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#define OBEX_TL_L2CAP_BT_HDR_MIN_OFFSET 13 /* L2CAP_MIN_OFFSET */
|
||||
|
||||
tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void);
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
764
components/bt/host/bluedroid/stack/obex/obex_api.c
Normal file
764
components/bt/host/bluedroid/stack/obex/obex_api.c
Normal file
@ -0,0 +1,764 @@
|
||||
/*
|
||||
* 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 "obex_int.h"
|
||||
#include "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
static inline void obex_server_to_tl_server(tOBEX_SVR_INFO *server, tOBEX_TL_SVR_INFO *tl_server)
|
||||
{
|
||||
if (server->tl == OBEX_OVER_L2CAP) {
|
||||
tl_server->l2cap.psm = server->l2cap.psm;
|
||||
tl_server->l2cap.sec_mask = server->l2cap.sec_mask;
|
||||
tl_server->l2cap.pref_mtu = server->l2cap.pref_mtu;
|
||||
bdcpy(tl_server->l2cap.addr, server->l2cap.addr);
|
||||
}
|
||||
else {
|
||||
OBEX_TRACE_ERROR("Unsupported OBEX transport type\n");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void obex_updata_packet_length(BT_HDR *p_buf, UINT16 len)
|
||||
{
|
||||
UINT8 *p_pkt_len = (UINT8 *)(p_buf + 1) + p_buf->offset + 1;
|
||||
STORE16BE(p_pkt_len, len);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_Init
|
||||
**
|
||||
** Description Initialize OBEX Profile, must call before using any other
|
||||
** OBEX APIs
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_Init(void)
|
||||
{
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (!obex_cb_ptr) {
|
||||
obex_cb_ptr = (tOBEX_CB *)osi_malloc(sizeof(tOBEX_CB));
|
||||
if (!obex_cb_ptr) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
memset(&obex_cb, 0, sizeof(tOBEX_CB));
|
||||
obex_cb.tl_ops[OBEX_OVER_L2CAP] = obex_tl_l2cap_ops_get();
|
||||
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 */
|
||||
/*
|
||||
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);
|
||||
}
|
||||
*/
|
||||
obex_cb.trace_level = BT_TRACE_LEVEL_ERROR;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_Deinit
|
||||
**
|
||||
** Description Deinit OBEX profile, once deinit, can not use any other
|
||||
** APIs until call OBEX_Init again
|
||||
**
|
||||
*******************************************************************************/
|
||||
void OBEX_Deinit(void)
|
||||
{
|
||||
if (obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_L2CAP]->deinit();
|
||||
}
|
||||
/*
|
||||
if (obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit != NULL) {
|
||||
obex_cb.tl_ops[OBEX_OVER_RFCOMM]->deinit();
|
||||
}
|
||||
*/
|
||||
#if (OBEX_DYNAMIC_MEMORY)
|
||||
if (obex_cb_ptr) {
|
||||
osi_free(obex_cb_ptr);
|
||||
obex_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (OBEX_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CreateConn
|
||||
**
|
||||
** Description Start the progress of creating an OBEX connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed, when the
|
||||
** connection is opened, an OBEX_CONNECT_EVT will come
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_CreateConn(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_handle)
|
||||
{
|
||||
UINT16 ret = OBEX_SUCCESS;
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
|
||||
do {
|
||||
if (server->tl >= OBEX_NUM_TL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = obex_allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
ret = OBEX_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
tOBEX_TL_SVR_INFO tl_server = {0};
|
||||
obex_server_to_tl_server(server, &tl_server);
|
||||
p_ccb->tl = server->tl;
|
||||
p_ccb->tl_hdl = obex_cb.tl_ops[p_ccb->tl]->connect(&tl_server);
|
||||
if (p_ccb->tl_hdl == 0) {
|
||||
ret = OBEX_ERROR_TL;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb->callback = callback;
|
||||
p_ccb->role = OBEX_ROLE_CLIENT;
|
||||
p_ccb->state = OBEX_STATE_OPENING;
|
||||
*out_handle = p_ccb->allocated;
|
||||
} while (0);
|
||||
|
||||
if (ret != OBEX_SUCCESS && p_ccb != NULL) {
|
||||
obex_free_ccb(p_ccb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_RemoveConn
|
||||
**
|
||||
** Description Remove an OBEX connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_RemoveConn(UINT16 handle)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
|
||||
return OBEX_BAD_HANDLE;
|
||||
}
|
||||
|
||||
p_ccb = &obex_cb.ccb[ccb_idx];
|
||||
obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl);
|
||||
obex_free_ccb(p_ccb);
|
||||
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_RegisterServer
|
||||
**
|
||||
** Description Register an OBEX server and listen the incoming connection
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_RegisterServer(tOBEX_SVR_INFO *server, tOBEX_MSG_CBACK callback, UINT16 *out_svr_handle)
|
||||
{
|
||||
UINT8 ret = OBEX_SUCCESS;
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
|
||||
do {
|
||||
if (server->tl >= OBEX_NUM_TL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
p_scb = obex_allocate_scb();
|
||||
if (p_scb == NULL) {
|
||||
ret = OBEX_NO_RESOURCES;
|
||||
break;
|
||||
}
|
||||
|
||||
tOBEX_TL_SVR_INFO tl_server = {0};
|
||||
obex_server_to_tl_server(server, &tl_server);
|
||||
p_scb->tl = server->tl;
|
||||
p_scb->tl_hdl = obex_cb.tl_ops[p_scb->tl]->bind(&tl_server);
|
||||
if (p_scb->tl_hdl == 0) {
|
||||
ret = OBEX_ERROR_TL;
|
||||
break;
|
||||
}
|
||||
p_scb->callback = callback;
|
||||
|
||||
if (out_svr_handle) {
|
||||
/* To avoid confuse with connection handle, left shift 8 bit */
|
||||
*out_svr_handle = p_scb->allocated << 8;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (ret != OBEX_SUCCESS && p_scb != NULL) {
|
||||
obex_free_scb(p_scb);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_DeregisterServer
|
||||
**
|
||||
** Description Deregister an OBEX server, if there are still a connection
|
||||
** alive, the behavior depend on the implementation of transport
|
||||
** layer
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_DeregisterServer(UINT16 svr_handle)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
|
||||
UINT16 scb_idx = (svr_handle >> 8) - 1;
|
||||
if (scb_idx >= OBEX_MAX_SERVER || !obex_cb.scb[scb_idx].allocated) {
|
||||
return OBEX_BAD_HANDLE;
|
||||
}
|
||||
|
||||
p_scb = &obex_cb.scb[scb_idx];
|
||||
obex_cb.tl_ops[p_scb->tl]->unbind(p_scb->tl_hdl);
|
||||
obex_free_scb(p_scb);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_SendPacket
|
||||
**
|
||||
** Description Send a packet to peer OBEX server or client, once call
|
||||
** this function, the ownership of pkt is lost, do not free
|
||||
** or modify the pkt any more
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_SendPacket(UINT16 handle, BT_HDR *pkt)
|
||||
{
|
||||
UINT16 ret = OBEX_SUCCESS;
|
||||
BOOLEAN free_pkt = true;
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
do {
|
||||
if (pkt == NULL) {
|
||||
ret = OBEX_INVALID_PARAM;
|
||||
break;
|
||||
}
|
||||
|
||||
UINT16 ccb_idx = handle - 1;
|
||||
if (ccb_idx >= OBEX_MAX_CONNECTION || !obex_cb.ccb[ccb_idx].allocated) {
|
||||
ret = OBEX_BAD_HANDLE;
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb = &obex_cb.ccb[ccb_idx];
|
||||
if (p_ccb->state != OBEX_STATE_OPENED) {
|
||||
ret = OBEX_NOT_OPEN;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pkt->len > p_ccb->tl_peer_mtu) {
|
||||
ret = OBEX_PACKET_TOO_LARGE;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = obex_cb.tl_ops[p_ccb->tl]->send(p_ccb->tl_hdl, pkt);
|
||||
/* packet has pass to lower layer, do not free it */
|
||||
free_pkt = false;
|
||||
if (ret == OBEX_TL_SUCCESS || ret == OBEX_TL_CONGESTED) {
|
||||
ret = OBEX_SUCCESS;
|
||||
}
|
||||
else {
|
||||
ret = OBEX_ERROR_TL;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if (free_pkt) {
|
||||
osi_free(pkt);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_BuildRequest
|
||||
**
|
||||
** Description Build a request packet with opcode and additional info,
|
||||
** packet can free by osi_free
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
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) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
buff_size += sizeof(BT_HDR) + OBEX_BT_HDR_MIN_OFFSET;
|
||||
|
||||
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
|
||||
if (p_buf == NULL) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
|
||||
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;
|
||||
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
|
||||
/* byte 0: opcode */
|
||||
*p_data++ = info->opcode;
|
||||
/* byte 1, 2: packet length, skip, we will update at last */
|
||||
UINT8 *p_pkt_len = p_data;
|
||||
p_data += 2;
|
||||
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
/* byte 3: OBEX version number */
|
||||
*p_data++ = info->obex_version_number;
|
||||
/* byte 4: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu*/
|
||||
STORE16BE(p_data, info->max_packet_length);
|
||||
pkt_len += 4;
|
||||
break;
|
||||
case OBEX_OPCODE_SETPATH:
|
||||
/* byte 3: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 4: constants, reserved, must be zero */
|
||||
*p_data++ = 0;
|
||||
pkt_len += 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
STORE16BE(p_pkt_len, pkt_len);
|
||||
p_buf->len = pkt_len;
|
||||
*out_pkt = p_buf;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_BuildResponse
|
||||
**
|
||||
** Description Build a response packet with opcode and response and additional
|
||||
** info, packet can by free by osi_free
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_BuildResponse(tOBEX_PARSE_INFO *info, UINT16 buff_size, BT_HDR **out_pkt)
|
||||
{
|
||||
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;
|
||||
|
||||
BT_HDR *p_buf= (BT_HDR *)osi_malloc(buff_size);
|
||||
if (p_buf == NULL) {
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
|
||||
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;
|
||||
UINT8 *p_data = (UINT8 *)(p_buf + 1) + p_buf->offset;
|
||||
/* byte 0: response code */
|
||||
*p_data++ = info->response_code;
|
||||
/* byte 1, 2: packet length, skip, we will update at last */
|
||||
UINT8 *p_pkt_len = p_data;
|
||||
p_data += 2;
|
||||
|
||||
/* we need to use opcode to decide the response format */
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
/* byte 3: OBEX version number */
|
||||
*p_data++ = info->obex_version_number;
|
||||
/* byte 4: flags */
|
||||
*p_data++ = info->flags;
|
||||
/* byte 5, 6: maximum OBEX packet length, recommend to set as our mtu */
|
||||
STORE16BE(p_data, info->max_packet_length);
|
||||
pkt_len += 4;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
STORE16BE(p_pkt_len, pkt_len);
|
||||
p_buf->len = pkt_len;
|
||||
*out_pkt = p_buf;
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeader
|
||||
**
|
||||
** Description Append a header to specific packet, packet can be request
|
||||
** or response, the format of header must be valid
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeader(BT_HDR *pkt, const UINT8 *header)
|
||||
{
|
||||
if (pkt == NULL || header == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT16 header_len = 0;
|
||||
UINT8 header_id = *header;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
header_len = (header[1] << 8) + header[2];
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (header_len == 0) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (pkt->layer_specific - pkt->len < header_len) {
|
||||
/* the packet can not hold this header */
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
UINT8 *p_start = p_data + pkt->len;
|
||||
memcpy(p_start, header, header_len);
|
||||
pkt->len += header_len;
|
||||
/* point to packet len */
|
||||
p_data++;
|
||||
STORE16BE(p_data, pkt->len);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderRaw
|
||||
**
|
||||
** Description Append a header to specific packet, packet can be request
|
||||
** or response, data not include 2 byte length prefixed
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderRaw(BT_HDR *pkt, UINT8 header_id, const UINT8 *data, UINT16 data_len)
|
||||
{
|
||||
if (pkt == NULL || data == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT16 header_len = 0;
|
||||
BOOLEAN store_header_len = FALSE;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
/* header id + 2 byte length prefixed + data */
|
||||
header_len = data_len + 3;
|
||||
store_header_len = TRUE;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
if (data_len != 1) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
if (data_len != 4) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (header_len == 0) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if (pkt->layer_specific - pkt->len < header_len) {
|
||||
/* the packet can not hold this header */
|
||||
return OBEX_NO_RESOURCES;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
UINT8 *p_start = p_data + pkt->len;
|
||||
/* store header id */
|
||||
*p_start++ = header_id;
|
||||
if (store_header_len) {
|
||||
/* store header length */
|
||||
STORE16BE(p_start, header_len);
|
||||
p_start+= 2;
|
||||
}
|
||||
/* store data */
|
||||
memcpy(p_start, data, data_len);
|
||||
pkt->len += header_len;
|
||||
/* point to packet len */
|
||||
p_data++;
|
||||
STORE16BE(p_data, pkt->len);
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderSRM
|
||||
**
|
||||
** Description Append a Single Response Mode header
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderSRM(BT_HDR *pkt, UINT8 value)
|
||||
{
|
||||
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM, &value, 1);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_AppendHeaderSRMP
|
||||
**
|
||||
** Description Append a Single Response Mode Parameters header
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_AppendHeaderSRMP(BT_HDR *pkt, UINT8 value)
|
||||
{
|
||||
return OBEX_AppendHeaderRaw(pkt, OBEX_HEADER_ID_SRM_PARAM, &value, 1);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetPacketFreeSpace
|
||||
**
|
||||
** Description Get the current free space of a packet, use this to check
|
||||
** if a packet can hold a header
|
||||
**
|
||||
** Returns Current free space of a packet, in bytes
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetPacketFreeSpace(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pkt->layer_specific - pkt->len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetPacketLength
|
||||
**
|
||||
** Description Get the current packet length
|
||||
**
|
||||
** Returns Current packet length, in bytes
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetPacketLength(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return pkt->len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_ParseRequest
|
||||
**
|
||||
** Description Parse a request packet
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_ParseRequest(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
info->opcode = *p_data;
|
||||
switch (info->opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
info->obex_version_number = p_data[3];
|
||||
info->flags = p_data[4];
|
||||
info->max_packet_length = (p_data[5] << 8) + p_data[6];
|
||||
info->next_header_pos = 7;
|
||||
break;
|
||||
case OBEX_OPCODE_SETPATH:
|
||||
info->flags = p_data[3];
|
||||
info->next_header_pos = 5;
|
||||
break;
|
||||
default:
|
||||
info->next_header_pos = 3;
|
||||
break;
|
||||
}
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_ParseResponse
|
||||
**
|
||||
** Description Parse a request response packet
|
||||
**
|
||||
** Returns OBEX_SUCCESS if successful, otherwise failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_ParseResponse(BT_HDR *pkt, UINT8 opcode, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return OBEX_INVALID_PARAM;
|
||||
}
|
||||
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
info->opcode = opcode;
|
||||
info->response_code = *p_data;
|
||||
switch (opcode)
|
||||
{
|
||||
case OBEX_OPCODE_CONNECT:
|
||||
info->obex_version_number = p_data[3];
|
||||
info->flags = p_data[4];
|
||||
info->max_packet_length = (p_data[5] << 8) + p_data[6];
|
||||
info->next_header_pos = 7;
|
||||
break;
|
||||
default:
|
||||
info->next_header_pos = 3;
|
||||
break;
|
||||
}
|
||||
return OBEX_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CheckFinalBit
|
||||
**
|
||||
** Description Check whether a packet had set the final bit
|
||||
**
|
||||
** Returns TRUE if final bit set, otherwise, false
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN OBEX_CheckFinalBit(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
return (*p_data) & OBEX_FINAL_BIT_MASK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_CheckContinueResponse
|
||||
**
|
||||
** Description Check whether a packet is continue response
|
||||
**
|
||||
** Returns TRUE if continue response, otherwise, false
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN OBEX_CheckContinueResponse(BT_HDR *pkt)
|
||||
{
|
||||
if (pkt == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
return (*p_data == 0x90) || (*p_data == 0x10);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetHeaderLength
|
||||
**
|
||||
** Description Get header length
|
||||
**
|
||||
** Returns header length
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 OBEX_GetHeaderLength(UINT8 *header)
|
||||
{
|
||||
UINT16 header_len = 0;
|
||||
UINT8 header_id = *header;
|
||||
switch (header_id & OBEX_HEADER_ID_U2B_MASK)
|
||||
{
|
||||
case OBEX_HEADER_ID_U2B_TYPE1:
|
||||
case OBEX_HEADER_ID_U2B_TYPE2:
|
||||
header_len = (header[1] << 8) + header[2];
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE3:
|
||||
header_len = 2;
|
||||
break;
|
||||
case OBEX_HEADER_ID_U2B_TYPE4:
|
||||
header_len = 5;
|
||||
break;
|
||||
default:
|
||||
/* unreachable */
|
||||
break;
|
||||
}
|
||||
return header_len;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function OBEX_GetNextHeader
|
||||
**
|
||||
** Description Get next header pointer from a packet
|
||||
**
|
||||
** Returns Pointer to start address of a header, NULL if no more header
|
||||
** or failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT8 *OBEX_GetNextHeader(BT_HDR *pkt, tOBEX_PARSE_INFO *info)
|
||||
{
|
||||
if (pkt == NULL || info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
UINT8 *p_data = (UINT8 *)(pkt + 1) + pkt->offset;
|
||||
if (info->next_header_pos == 0 || info->next_header_pos >= pkt->len) {
|
||||
return NULL;
|
||||
}
|
||||
UINT8 *header = p_data + info->next_header_pos;
|
||||
UINT16 header_len = OBEX_GetHeaderLength(header);
|
||||
info->next_header_pos += header_len;
|
||||
return header;
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
211
components/bt/host/bluedroid/stack/obex/obex_main.c
Normal file
211
components/bt/host/bluedroid/stack/obex/obex_main.c
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 "obex_int.h"
|
||||
#include "obex_tl.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
tOBEX_CB obex_cb;
|
||||
#else
|
||||
tOBEX_CB *obex_cb_ptr = NULL;
|
||||
#endif
|
||||
|
||||
static tOBEX_CCB *obex_find_ccb_by_tl_hdl(UINT8 tl, UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_CONNECTION; ++i) {
|
||||
if (obex_cb.ccb[i].allocated && obex_cb.ccb[i].tl == tl && obex_cb.ccb[i].tl_hdl == tl_hdl) {
|
||||
p_ccb = &obex_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_SCB *obex_find_scb_by_tl_hdl(UINT8 tl, UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_SERVER; ++i) {
|
||||
if (obex_cb.scb[i].allocated && obex_cb.scb[i].tl == tl && obex_cb.scb[i].tl_hdl == tl_hdl) {
|
||||
p_scb = &obex_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
tOBEX_CCB *obex_allocate_ccb(void)
|
||||
{
|
||||
tOBEX_CCB *p_ccb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_CONNECTION; ++i) {
|
||||
if (!obex_cb.ccb[i].allocated) {
|
||||
obex_cb.ccb[i].allocated = i + 1;
|
||||
p_ccb = &obex_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
void obex_free_ccb(tOBEX_CCB *p_ccb)
|
||||
{
|
||||
assert(p_ccb->allocated != 0);
|
||||
memset(p_ccb, 0, sizeof(tOBEX_CCB));
|
||||
}
|
||||
|
||||
tOBEX_SCB *obex_allocate_scb(void)
|
||||
{
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
for (int i = 0; i < OBEX_MAX_SERVER; ++i) {
|
||||
if (!obex_cb.scb[i].allocated) {
|
||||
obex_cb.scb[i].allocated = i + 1;
|
||||
p_scb = &obex_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
void obex_free_scb(tOBEX_SCB *p_scb)
|
||||
{
|
||||
assert(p_scb->allocated != 0);
|
||||
memset(p_scb, 0, sizeof(tOBEX_SCB));
|
||||
}
|
||||
|
||||
/* check whether connection mtu is valid, if not, disconnect it */
|
||||
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");
|
||||
obex_cb.tl_ops[p_ccb->tl]->disconnect(p_ccb->tl_hdl);
|
||||
obex_free_ccb(p_ccb);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void obex_tl_evt_handler(UINT8 tl, tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
|
||||
{
|
||||
UINT16 tl_hdl = msg->any.hdl;
|
||||
tOBEX_CCB *p_ccb = obex_find_ccb_by_tl_hdl(tl, tl_hdl);
|
||||
tOBEX_SCB *p_scb = NULL;
|
||||
tOBEX_MSG cb_msg = {0};
|
||||
|
||||
switch (evt)
|
||||
{
|
||||
case OBEX_TL_CONN_OPEN_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
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)) {
|
||||
break;
|
||||
}
|
||||
p_ccb->state = OBEX_STATE_OPENED;
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.connect.peer_mtu = msg->conn_open.peer_mtu;
|
||||
cb_msg.connect.our_mtu = msg->conn_open.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_CONNECT_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_DIS_CONN_EVT:
|
||||
if (p_ccb != NULL) {
|
||||
if (p_ccb->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_DISCONNECT_EVT, NULL);
|
||||
}
|
||||
obex_free_ccb(p_ccb);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_CONGEST_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
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->callback) {
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_UNCONGEST_EVT, NULL);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_MTU_CHANGE_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
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)) {
|
||||
break;
|
||||
}
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.mtu_change.peer_mtu = msg->mtu_chg.peer_mtu;
|
||||
cb_msg.mtu_change.our_mtu = msg->mtu_chg.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_MTU_CHANGE_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_DATA_EVT:
|
||||
assert(p_ccb != NULL);
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.data.pkt = msg->data.p_buf;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_DATA_EVT, &cb_msg);
|
||||
}
|
||||
else {
|
||||
/* No callback, just free the packet */
|
||||
osi_free(msg->data.p_buf);
|
||||
}
|
||||
break;
|
||||
case OBEX_TL_CONN_INCOME_EVT:
|
||||
/* New connection, p_ccb should be NULL */
|
||||
assert(p_ccb == NULL);
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((p_ccb = obex_allocate_ccb()) == NULL) {
|
||||
obex_cb.tl_ops[tl]->disconnect(tl_hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
p_ccb->tl = tl;
|
||||
p_ccb->tl_hdl = tl_hdl;
|
||||
p_ccb->role = OBEX_ROLE_SERVER;
|
||||
p_ccb->tl_peer_mtu = msg->conn_income.peer_mtu;
|
||||
p_ccb->tl_our_mtu = msg->conn_income.our_mtu;
|
||||
if (!check_conn_mtu_valid(p_ccb, FALSE)) {
|
||||
break;
|
||||
}
|
||||
p_ccb->state = OBEX_STATE_OPENED;
|
||||
p_ccb->callback = p_scb->callback;
|
||||
if (p_ccb->callback) {
|
||||
cb_msg.conn_income.svr_handle = p_scb->allocated << 8;
|
||||
cb_msg.conn_income.peer_mtu = msg->conn_income.peer_mtu;
|
||||
cb_msg.conn_income.our_mtu = msg->conn_income.our_mtu;
|
||||
p_ccb->callback(p_ccb->allocated, OBEX_CONN_INCOME_EVT, &cb_msg);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OBEX_TRACE_ERROR("Unknown OBEX transport event: 0x%x\n", evt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void obex_tl_l2cap_callback(tOBEX_TL_EVT evt, tOBEX_TL_MSG *msg)
|
||||
{
|
||||
obex_tl_evt_handler(OBEX_OVER_L2CAP, evt, msg);
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
807
components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c
Normal file
807
components/bt/host/bluedroid/stack/obex/obex_tl_l2cap.c
Normal file
@ -0,0 +1,807 @@
|
||||
/*
|
||||
* 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/l2c_api.h"
|
||||
#include "stack/l2cdefs.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "btm_int.h"
|
||||
#include "obex_tl.h"
|
||||
#include "obex_tl_l2cap.h"
|
||||
|
||||
#if (OBEX_INCLUDED == TRUE)
|
||||
|
||||
#define OBEX_TL_L2CAP_NUM_CONN 4
|
||||
#define OBEX_TL_L2CAP_NUM_SERVER 2
|
||||
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE 0x01
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE 0x02
|
||||
#define OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED 0x80
|
||||
|
||||
/* ERTM Tx window size */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_TX_WINDOW_SIZE 10
|
||||
|
||||
/* ERTM Maximum transmissions before disconnecting */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MAX_TX_B4_DISCNT 20
|
||||
|
||||
/* ERTM Retransmission timeout (2 secs) */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_RETX_TOUT 2000
|
||||
|
||||
/* ERTM Monitor timeout (12 secs) */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MONITOR_TOUT 12000
|
||||
|
||||
/* ERTM ERTM MPS segment size */
|
||||
#define OBEX_TL_L2CAP_FCR_OPT_MAX_PDU_SIZE 1010
|
||||
|
||||
typedef struct {
|
||||
UINT16 vpsm; /* psm in our side */
|
||||
UINT16 lcid; /* local channel id */
|
||||
UINT16 peer_mtu; /* MTU that peer device set */
|
||||
UINT16 our_mtu; /* MTU that we set to peer device */
|
||||
BOOLEAN initiator; /* TRUE if is initiator, otherwise FALSE */
|
||||
UINT8 id; /* remote channel id */
|
||||
UINT8 status_flag; /* status flag used in config procedure */
|
||||
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, equal to handle */
|
||||
BD_ADDR addr; /* peer bluetooth device address */
|
||||
} tOBEX_TL_L2CAP_CCB;
|
||||
|
||||
typedef struct {
|
||||
UINT16 psm; /* psm in our side */
|
||||
UINT16 pref_mtu; /* preferred mtu */
|
||||
UINT8 allocated; /* 0 if not allocated, otherwise, index + 1, handle of server will left shift 8 bits */
|
||||
} tOBEX_TL_L2CAP_SCB;
|
||||
|
||||
typedef struct {
|
||||
tOBEX_TL_CBACK *callback; /* Upper layer callback */
|
||||
tOBEX_TL_L2CAP_CCB ccb[OBEX_TL_L2CAP_NUM_CONN];
|
||||
tOBEX_TL_L2CAP_SCB scb[OBEX_TL_L2CAP_NUM_SERVER];
|
||||
tL2CAP_APPL_INFO l2cap_reg_info; /* Default L2CAP Registration info */
|
||||
UINT8 trace_level; /* trace level */
|
||||
} tOBEX_TL_L2CAP_CB;
|
||||
|
||||
#if OBEX_DYNAMIC_MEMORY == FALSE
|
||||
static tOBEX_TL_L2CAP_CB obex_tl_l2cap_cb;
|
||||
#else
|
||||
static tOBEX_TL_L2CAP_CB *obex_tl_l2cap_cb_ptr = NULL;
|
||||
#define obex_tl_l2cap_cb (*obex_tl_l2cap_cb_ptr)
|
||||
#endif
|
||||
|
||||
static tL2CAP_ERTM_INFO obex_tl_l2cap_etm_opts =
|
||||
{
|
||||
L2CAP_FCR_ERTM_MODE,
|
||||
L2CAP_FCR_CHAN_OPT_ERTM | L2CAP_FCR_CHAN_OPT_BASIC, /* Some devices do not support ERTM */
|
||||
L2CAP_USER_RX_BUF_SIZE,
|
||||
L2CAP_USER_TX_BUF_SIZE,
|
||||
L2CAP_FCR_RX_BUF_SIZE,
|
||||
L2CAP_FCR_TX_BUF_SIZE
|
||||
};
|
||||
|
||||
static tL2CAP_FCR_OPTS obex_tl_l2cap_fcr_opts =
|
||||
{
|
||||
L2CAP_FCR_ERTM_MODE,
|
||||
OBEX_TL_L2CAP_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */
|
||||
OBEX_TL_L2CAP_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
|
||||
OBEX_TL_L2CAP_FCR_OPT_MAX_PDU_SIZE /* MPS segment size */
|
||||
};
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *allocate_ccb(void)
|
||||
{
|
||||
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 == 0) {
|
||||
obex_tl_l2cap_cb.ccb[i].allocated = i + 1;
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static void free_ccb(tOBEX_TL_L2CAP_CCB *p_ccb)
|
||||
{
|
||||
memset(p_ccb, 0, sizeof(tOBEX_TL_L2CAP_CCB));
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *find_ccb_by_lcid(UINT16 lcid)
|
||||
{
|
||||
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].lcid == lcid) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_CCB *find_ccb_by_hdl(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
if (hdl > 0 && hdl <= OBEX_TL_L2CAP_NUM_CONN) {
|
||||
if (obex_tl_l2cap_cb.ccb[hdl-1].allocated == hdl) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[hdl-1];
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
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) {
|
||||
p_ccb = &obex_tl_l2cap_cb.ccb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_ccb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *allocate_scb(void)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_SERVER; ++i) {
|
||||
if (obex_tl_l2cap_cb.scb[i].allocated == 0) {
|
||||
obex_tl_l2cap_cb.scb[i].allocated = i + 1;
|
||||
p_scb = &obex_tl_l2cap_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *find_scb_by_psm(UINT16 psm)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
for(int i = 0; i < OBEX_TL_L2CAP_NUM_SERVER; ++i) {
|
||||
if (obex_tl_l2cap_cb.scb[i].allocated && obex_tl_l2cap_cb.scb[i].psm == psm) {
|
||||
p_scb = &obex_tl_l2cap_cb.scb[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
static void free_scb(tOBEX_TL_L2CAP_SCB *p_scb)
|
||||
{
|
||||
memset(p_scb, 0, sizeof(tOBEX_TL_L2CAP_SCB));
|
||||
}
|
||||
|
||||
static tOBEX_TL_L2CAP_SCB *find_scb_by_hdl(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = NULL;
|
||||
hdl = hdl >> 8;
|
||||
if (hdl > 0 && hdl <= OBEX_TL_L2CAP_NUM_SERVER) {
|
||||
if (obex_tl_l2cap_cb.scb[hdl-1].allocated == hdl) {
|
||||
p_scb = &obex_tl_l2cap_cb.scb[hdl-1];
|
||||
}
|
||||
}
|
||||
return p_scb;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function obex_tl_l2cap_sec_check_complete_term
|
||||
*
|
||||
* Description OBEX over L2CAP security check complete callback function
|
||||
* (terminated).
|
||||
*
|
||||
******************************************************************************/
|
||||
static void l2cap_sec_check_complete_term(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data;
|
||||
|
||||
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};
|
||||
cfg.mtu_present = TRUE;
|
||||
cfg.mtu = p_ccb->our_mtu;
|
||||
cfg.fcr_present = TRUE;
|
||||
cfg.fcr = obex_tl_l2cap_fcr_opts;
|
||||
L2CA_ConfigReq(p_ccb->lcid, &cfg);
|
||||
}
|
||||
else{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function obex_tl_l2cap_sec_check_complete_orig
|
||||
*
|
||||
* Description OBEX over L2CAP security check complete callback function
|
||||
* (originated).
|
||||
*
|
||||
******************************************************************************/
|
||||
static void l2cap_sec_check_complete_orig(BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = (tOBEX_TL_L2CAP_CCB *)p_ref_data;
|
||||
|
||||
if (res == BTM_SUCCESS) {
|
||||
tL2CAP_CFG_INFO cfg = {0};
|
||||
cfg.mtu_present = TRUE;
|
||||
cfg.mtu = p_ccb->our_mtu;
|
||||
cfg.fcr_present = TRUE;
|
||||
cfg.fcr = obex_tl_l2cap_fcr_opts;
|
||||
L2CA_ConfigReq(p_ccb->lcid, &cfg);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConnectInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_connect_ind(BD_ADDR addr, UINT16 lcid, UINT16 psm, UINT8 id)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(psm);
|
||||
if (p_scb == NULL) {
|
||||
/* An incoming connection, but no corresponding server found, reject */
|
||||
L2CA_ErtmConnectRsp (addr, id, lcid, L2CAP_CONN_NO_PSM, 0, &obex_tl_l2cap_etm_opts);
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("No corresponding server found, reject incoming connection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
/* No resource, can not allocate ccb, reject */
|
||||
L2CA_ErtmConnectRsp (addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0, &obex_tl_l2cap_etm_opts);
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("Can not allocate a ccb for new connection\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bdcpy(p_ccb->addr, addr);
|
||||
p_ccb->initiator = FALSE;
|
||||
p_ccb->lcid = lcid;
|
||||
p_ccb->id = id;
|
||||
p_ccb->vpsm = psm;
|
||||
p_ccb->our_mtu = p_scb->pref_mtu;
|
||||
if (btm_sec_mx_access_request(p_ccb->addr, p_ccb->vpsm,
|
||||
FALSE, BTM_SEC_PROTO_OBEX,
|
||||
OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1,
|
||||
&l2cap_sec_check_complete_term, p_ccb) == BTM_CMD_STARTED) {
|
||||
L2CA_ErtmConnectRsp(addr, id, lcid, L2CAP_CONN_PENDING, 0, &obex_tl_l2cap_etm_opts);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect_cfm
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** ConnectCfm received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_connect_cfm(UINT16 lcid, UINT16 result)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_connect_cfm but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == L2CAP_CONN_OK) {
|
||||
btm_sec_mx_access_request(p_ccb->addr, p_ccb->vpsm,
|
||||
TRUE, BTM_SEC_PROTO_OBEX,
|
||||
p_ccb->allocated - 1,
|
||||
&l2cap_sec_check_complete_orig, p_ccb);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_config_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConfigInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_config_ind(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(p_ccb->vpsm);
|
||||
if (p_ccb->initiator == FALSE && p_scb == NULL) {
|
||||
/* not a initiator, but can not find corresponding server */
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_ind, not a initiator, but can not find corresponding server\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* save peer mtu if present */
|
||||
UINT16 peer_mtu = L2CAP_DEFAULT_MTU;
|
||||
if (p_cfg->mtu_present) {
|
||||
peer_mtu = p_cfg->mtu;
|
||||
}
|
||||
|
||||
p_cfg->mtu_present = FALSE;
|
||||
p_cfg->flush_to_present = FALSE;
|
||||
p_cfg->qos_present = FALSE;
|
||||
p_cfg->result = L2CAP_CFG_OK;
|
||||
L2CA_ConfigRsp(p_ccb->lcid, p_cfg);
|
||||
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED) {
|
||||
/* reconfig l2cap channel */
|
||||
if (p_ccb->peer_mtu != peer_mtu) {
|
||||
/* only report to upper if mtu change */
|
||||
p_ccb->peer_mtu = peer_mtu;
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.mtu_chg.hdl = p_ccb->allocated;
|
||||
msg.mtu_chg.peer_mtu = peer_mtu;
|
||||
msg.mtu_chg.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_MTU_CHANGE_EVT, &msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
p_ccb->peer_mtu = peer_mtu;
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE;
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE) {
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED;
|
||||
}
|
||||
|
||||
if ((p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED)) {
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
if (p_ccb->initiator) {
|
||||
msg.conn_open.hdl = p_ccb->allocated;
|
||||
msg.conn_open.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_open.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
msg.conn_income.hdl = p_ccb->allocated;
|
||||
msg.conn_income.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_income.our_mtu = p_ccb->our_mtu;
|
||||
msg.conn_income.svr_hdl = p_scb->allocated << 8;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_config_cfm
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_ConfigCnf received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_config_cfm(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_config_cfm but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(p_ccb->vpsm);
|
||||
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");
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_cfg->result != L2CAP_CFG_OK) {
|
||||
OBEX_TL_L2CAP_TRACE_WARNING("l2cap_config_cfm result != L2CAP_CFG_OK: result: 0x%x\n", p_cfg->result);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CFG_DONE;
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_PEER_CFG_DONE) {
|
||||
p_ccb->status_flag |= OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED;
|
||||
}
|
||||
|
||||
if (p_ccb->status_flag & OBEX_TL_L2CAP_STATUS_FLAG_CONNECTED) {
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
if (p_ccb->initiator) {
|
||||
msg.conn_open.hdl = p_ccb->allocated;
|
||||
msg.conn_open.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_open.our_mtu = p_ccb->our_mtu;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_OPEN_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
msg.conn_income.hdl = p_ccb->allocated;
|
||||
msg.conn_income.peer_mtu = p_ccb->peer_mtu;
|
||||
msg.conn_income.our_mtu = p_ccb->our_mtu;
|
||||
msg.conn_income.svr_hdl = p_scb->allocated << 8;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONN_INCOME_EVT, &msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_qos_violation_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_QoSViolationIndInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_qos_violation_ind(BD_ADDR addr)
|
||||
{
|
||||
UNUSED(addr);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_disconnect_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CA_DisconnectInd received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_disconnect_ind(UINT16 lcid, BOOLEAN is_conf_needed)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
|
||||
if (is_conf_needed) {
|
||||
L2CA_DisconnectRsp(lcid);
|
||||
}
|
||||
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_disconnect_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_buf_data_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** data frame is received.
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_buf_data_ind(UINT16 lcid, BT_HDR *p_buf)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_buf_data_ind but no ccb found\n");
|
||||
osi_free(p_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.data.hdl = p_ccb->allocated;
|
||||
msg.data.p_buf = p_buf;
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_DATA_EVT, &msg);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_congestion_status_ind
|
||||
**
|
||||
** Description This is a callback function called by L2CAP when
|
||||
** L2CAP channel congestion status changes
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_congestion_status_ind(UINT16 lcid, BOOLEAN is_congested)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_lcid(lcid);
|
||||
|
||||
if (p_ccb == NULL) {
|
||||
OBEX_TL_L2CAP_TRACE_ERROR("l2cap_congestion_status_ind but no ccb found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tOBEX_TL_MSG msg = {0};
|
||||
msg.any.hdl = p_ccb->allocated;
|
||||
if (is_congested) {
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_CONGEST_EVT, &msg);
|
||||
}
|
||||
else {
|
||||
obex_tl_l2cap_cb.callback(OBEX_TL_UNCONGEST_EVT, &msg);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** 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
|
||||
**
|
||||
*******************************************************************************/
|
||||
void 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");
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_init
|
||||
**
|
||||
** 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) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_connect
|
||||
**
|
||||
** Description Start the process of establishing a L2CAP connection
|
||||
**
|
||||
** Returns Non-zeros handle, 0 while failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_connect(tOBEX_TL_SVR_INFO *server)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = allocate_ccb();
|
||||
if (p_ccb == NULL) {
|
||||
/* can not allocate a ccb */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (find_scb_by_psm(server->l2cap.psm) == NULL) {
|
||||
/* if the psm not register by a server, we register it as outgoing only record */
|
||||
tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info;
|
||||
p_reg_info->pL2CA_ConnectInd_Cb = NULL;
|
||||
p_ccb->vpsm = L2CA_Register(server->l2cap.psm, p_reg_info);
|
||||
if (p_ccb->vpsm == 0) {
|
||||
free_ccb(p_ccb);
|
||||
return 0;
|
||||
}
|
||||
/* set security level */
|
||||
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_ccb->vpsm, BTM_SEC_PROTO_OBEX, p_ccb->allocated - 1);
|
||||
}
|
||||
else {
|
||||
p_ccb->vpsm = server->l2cap.psm;
|
||||
}
|
||||
|
||||
if (server->l2cap.pref_mtu == 0 || server->l2cap.pref_mtu > L2CAP_MTU_SIZE) {
|
||||
p_ccb->our_mtu = L2CAP_MTU_SIZE;
|
||||
}
|
||||
else {
|
||||
p_ccb->our_mtu = server->l2cap.pref_mtu;
|
||||
}
|
||||
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) {
|
||||
free_ccb(p_ccb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return p_ccb->allocated;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_disconnect
|
||||
**
|
||||
** Description Disconnect a L2CAP connection
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_disconnect(UINT16 hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_hdl(hdl);
|
||||
if (p_ccb != NULL) {
|
||||
L2CA_DisconnectReq(p_ccb->lcid);
|
||||
if (p_ccb->initiator && find_scb_by_psm(p_ccb->vpsm) == NULL) {
|
||||
L2CA_Deregister(p_ccb->vpsm);
|
||||
}
|
||||
free_ccb(p_ccb);
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_send_data
|
||||
**
|
||||
** Description Start the process of establishing a L2CAP connection
|
||||
**
|
||||
** Returns OBEX_TL_SUCCESS, if data accepted
|
||||
** OBEX_TL_CONGESTED, if data accepted and the channel is congested
|
||||
** OBEX_TL_FAILED, if error
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_send_data(UINT16 hdl, BT_HDR *p_buf)
|
||||
{
|
||||
UINT16 ret = OBEX_TL_FAILED;
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = find_ccb_by_hdl(hdl);
|
||||
if (p_ccb == NULL) {
|
||||
osi_free(p_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Can not send data size larger than peer MTU */
|
||||
/* Offset should not smaller than L2CAP_MIN_OFFSET */
|
||||
if (p_buf->len > p_ccb->peer_mtu || p_buf->offset < L2CAP_MIN_OFFSET) {
|
||||
osi_free(p_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
UINT16 status = L2CA_DataWrite(p_ccb->lcid, p_buf);
|
||||
switch (status)
|
||||
{
|
||||
case L2CAP_DW_SUCCESS:
|
||||
ret = OBEX_TL_SUCCESS;
|
||||
break;
|
||||
case L2CAP_DW_CONGESTED:
|
||||
ret = OBEX_TL_CONGESTED;
|
||||
break;
|
||||
default:
|
||||
ret = OBEX_TL_FAILED;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_bind
|
||||
**
|
||||
** Description Register a server in L2CAP module
|
||||
**
|
||||
** Returns Non-zeros handle, 0 while failed
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT16 obex_tl_l2cap_bind(tOBEX_TL_SVR_INFO *server)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_psm(server->l2cap.psm);
|
||||
|
||||
if (p_scb != NULL) {
|
||||
/* psm already used */
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_scb = allocate_scb();
|
||||
if (p_scb == NULL) {
|
||||
/* can not allocate a new scb */
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (server->l2cap.pref_mtu == 0 || server->l2cap.pref_mtu > L2CAP_MTU_SIZE) {
|
||||
p_scb->pref_mtu= L2CAP_MTU_SIZE;
|
||||
}
|
||||
else {
|
||||
p_scb->pref_mtu = server->l2cap.pref_mtu;
|
||||
}
|
||||
|
||||
tL2CAP_APPL_INFO *p_reg_info = &obex_tl_l2cap_cb.l2cap_reg_info;
|
||||
p_reg_info->pL2CA_ConnectInd_Cb = obex_tl_l2cap_connect_ind;
|
||||
/* Register a l2cap server, in this case, L2CA_Register always return the same psm as input */
|
||||
p_scb->psm = L2CA_Register(server->l2cap.psm, p_reg_info);
|
||||
if (p_scb->psm == 0) {
|
||||
free_scb(p_scb);
|
||||
return 0;
|
||||
}
|
||||
/* set security level */
|
||||
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_scb->psm, BTM_SEC_PROTO_OBEX, OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1);
|
||||
BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_OBEX, server->l2cap.sec_mask, p_scb->psm, BTM_SEC_PROTO_OBEX, OBEX_TL_L2CAP_NUM_CONN + p_scb->allocated - 1);
|
||||
/* left shift 8 bits to avoid confuse with connection handle */
|
||||
return p_scb->allocated << 8;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_unbind
|
||||
**
|
||||
** Description Deregister a server in L2CAP module
|
||||
**
|
||||
*******************************************************************************/
|
||||
void obex_tl_l2cap_unbind(UINT16 tl_hdl)
|
||||
{
|
||||
tOBEX_TL_L2CAP_SCB *p_scb = find_scb_by_hdl(tl_hdl);
|
||||
if (p_scb) {
|
||||
tOBEX_TL_L2CAP_CCB *p_ccb = NULL;
|
||||
while ((p_ccb = find_ccb_by_psm(p_scb->psm)) != NULL) {
|
||||
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);
|
||||
}
|
||||
L2CA_Deregister(p_scb->psm);
|
||||
free_scb(p_scb);
|
||||
}
|
||||
}
|
||||
|
||||
static tOBEX_TL_OPS obex_tl_l2cap_ops = {
|
||||
.init = obex_tl_l2cap_init,
|
||||
.deinit = obex_tl_l2cap_deinit,
|
||||
.connect = obex_tl_l2cap_connect,
|
||||
.disconnect = obex_tl_l2cap_disconnect,
|
||||
.bind = obex_tl_l2cap_bind,
|
||||
.unbind = obex_tl_l2cap_unbind,
|
||||
.send = obex_tl_l2cap_send_data
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function obex_tl_l2cap_ops_get
|
||||
**
|
||||
** Description Get the operation function structure pointer of OBEX over
|
||||
** L2CAP transport layer
|
||||
**
|
||||
** Returns Pointer to operation function structure
|
||||
**
|
||||
*******************************************************************************/
|
||||
tOBEX_TL_OPS *obex_tl_l2cap_ops_get(void)
|
||||
{
|
||||
return &obex_tl_l2cap_ops;
|
||||
}
|
||||
|
||||
#endif /* #if (OBEX_INCLUDED == TRUE) */
|
Loading…
Reference in New Issue
Block a user