diff options
Diffstat (limited to 'stack/obx')
-rw-r--r-- | stack/obx/hdrs/obx_dauth.c | 149 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_dbtp.c | 189 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_dopt.c | 52 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_dunic.c | 64 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_dutf.c | 91 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_dwchar.c | 91 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_ebtp.c | 177 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_eopt.c | 51 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_eunic.c | 64 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_eutf.c | 111 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_ewchar.c | 100 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_gen.c | 896 | ||||
-rw-r--r-- | stack/obx/hdrs/obx_wchar.c | 84 | ||||
-rw-r--r-- | stack/obx/hdrs/utfc.c | 245 | ||||
-rw-r--r-- | stack/obx/obx_cact.c | 1103 | ||||
-rw-r--r-- | stack/obx/obx_capi.c | 855 | ||||
-rw-r--r-- | stack/obx/obx_csm.c | 365 | ||||
-rw-r--r-- | stack/obx/obx_int.h | 602 | ||||
-rw-r--r-- | stack/obx/obx_l2c.c | 1031 | ||||
-rw-r--r-- | stack/obx/obx_main.c | 946 | ||||
-rw-r--r-- | stack/obx/obx_md5.c | 1050 | ||||
-rw-r--r-- | stack/obx/obx_rfc.c | 613 | ||||
-rw-r--r-- | stack/obx/obx_sact.c | 1519 | ||||
-rw-r--r-- | stack/obx/obx_sapi.c | 591 | ||||
-rw-r--r-- | stack/obx/obx_ssm.c | 365 | ||||
-rw-r--r-- | stack/obx/obx_utils.c | 979 |
26 files changed, 12383 insertions, 0 deletions
diff --git a/stack/obx/hdrs/obx_dauth.c b/stack/obx/hdrs/obx_dauth.c new file mode 100644 index 0000000..91d4bd7 --- /dev/null +++ b/stack/obx/hdrs/obx_dauth.c @@ -0,0 +1,149 @@ +/***************************************************************************** +** +** Name: obx_dauth.c +** +** File: OBEX Authentication related functions +** +** Copyright (c) 2003-2008, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "obx_api.h" + + + + + +#if (OBX_CLIENT_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function OBX_ReadChallenge +** +** Description This function is called by the client to read the Realm and +** options of the Authentication Challenge Header in the +** given OBEX packet ( from an OBX_PASSWORD_EVT event). +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadChallenge(BT_HDR *p_pkt, tOBX_CHARSET *p_charset, + UINT8 **p_realm, UINT8 *p_len, + tOBX_AUTH_OPT *p_option) +{ + BOOLEAN status; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + UINT8 xx; + + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if it returns TRUE. + * leave this unnecessary memset here */ + memset( triplet, 0, sizeof(triplet )); + + *p_len = 0; + *p_option = OBX_AO_NONE; + *p_realm = NULL; + *p_charset = OBX_RCS_ASCII; /* 0 */ + status = OBX_ReadTriplet(p_pkt, OBX_HI_CHALLENGE, triplet, &num_trip); + if(status) + { + for(xx=0; xx<num_trip; xx++) + { + switch(triplet[xx].tag) + { + case OBX_OPTIONS_CHLNG_TAG: + *p_option = triplet[xx].p_array[0]; + break; + case OBX_REALM_CHLNG_TAG: + *p_charset = triplet[xx].p_array[0]; + *p_len = triplet[xx].len - 1; + *p_realm = &(triplet[xx].p_array[1]); + break; + /* The user does not need to know the nonce value + case OBX_NONCE_CHLNG_TAG: + break; + */ + } + } + } + + return status; +} +#endif + + + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** +** Function OBX_ReadAuthParams +** +** Description This function is called by the server to read the User ID of +** the Authentication Response Header in the given OBEX packet +** ( from an OBX_PASSWORD_EVT event). +** This function also +** checks if the authentication challenge header exists and +** checks the authentication challenge options. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadAuthParams(BT_HDR *p_pkt, UINT8 **p_userid, UINT8 *p_len, + BOOLEAN *is_challenged, tOBX_AUTH_OPT *p_option) +{ + BOOLEAN status; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + UINT8 xx; + + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if it returns TRUE. + * leave this unnecessary memset here */ + memset( triplet, 0, sizeof(triplet )); + + *p_userid = NULL; + *p_len = 0; + *is_challenged = FALSE; + *p_option = OBX_AO_NONE; + status = OBX_ReadTriplet(p_pkt, OBX_HI_AUTH_RSP, triplet, &num_trip); + if(status) + { + /* found authentication response, but we do not know if it has user id. + * assume no user id */ + *p_len = 0; + for(xx=0; xx<num_trip; xx++) + { + if(triplet[xx].tag == OBX_USERID_RSP_TAG) + { + *p_len = triplet[xx].len; + *p_userid = triplet[xx].p_array; + break; + } + } + + /* read the authentication challenge header, + * only if the authentication response header exists */ + if( OBX_ReadTriplet(p_pkt, OBX_HI_CHALLENGE, triplet, &num_trip) == TRUE) + { + *is_challenged = TRUE; + for(xx=0; xx<num_trip; xx++) + { + if(triplet[xx].tag == OBX_OPTIONS_CHLNG_TAG) + { + *p_option = triplet[xx].p_array[0]; + break; + } + } + } + } + return status; +} +#endif + + + diff --git a/stack/obx/hdrs/obx_dbtp.c b/stack/obx/hdrs/obx_dbtp.c new file mode 100644 index 0000000..fbf473d --- /dev/null +++ b/stack/obx/hdrs/obx_dbtp.c @@ -0,0 +1,189 @@ +/***************************************************************************** +** +** Name: obx_dbtp.c +** +** File: OBEX Headers Decode Utility functions +** - for current Bluetooth profiles +** +** Copyright (c) 2003-2008, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "obx_api.h" +#include "obx_int.h" + + + + +/******************************************************************************* +** +** Function OBX_ReadTypeHdr +** +** Description This function is called to get the Type Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadTypeHdr(BT_HDR *p_pkt, UINT8 **p_type, UINT16 *p_len) +{ + return OBX_ReadByteStrHdr(p_pkt, OBX_HI_TYPE, p_type, p_len, 0); +} + +/******************************************************************************* +** +** Function OBX_ReadLengthHdr +** +** Description This function is called to get the Length Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadLengthHdr(BT_HDR *p_pkt, UINT32 *p_len) +{ + return OBX_Read4ByteHdr(p_pkt, OBX_HI_LENGTH, p_len); +} + +/******************************************************************************* +** +** Function OBX_ReadTargetHdr +** +** Description This function is called to get the Target Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadTargetHdr(BT_HDR *p_pkt, UINT8 **p_target, UINT16 *p_len, UINT8 next) +{ + return OBX_ReadByteStrHdr(p_pkt, OBX_HI_TARGET, p_target, p_len, next); +} + +/******************************************************************************* +** +** Function OBX_ReadBodyHdr +** +** Description This function is called to get the Body Header in the +** given OBEX packet. +** +** Returns 1, if a single header is in the OBEX packet. +** 2, if a end of body header is in the OBEX packet. +** 0, (FALSE) otherwise. +** +*******************************************************************************/ +UINT8 OBX_ReadBodyHdr(BT_HDR *p_pkt, UINT8 **p_body, UINT16 *p_len, BOOLEAN *p_end) +{ + BOOLEAN status; + UINT8 *p_body2; + UINT16 len2; + UINT8 num; + + *p_end = FALSE; + num = OBX_ReadByteStrHdr(p_pkt, OBX_HI_BODY, p_body, p_len, 0); + status = OBX_ReadByteStrHdr(p_pkt, OBX_HI_BODY_END, &p_body2, &len2, 0); + if(num == FALSE && status == TRUE) + { + *p_body = p_body2; + *p_len = len2; + *p_end = TRUE; + } + num += status; + + return num; +} + +/******************************************************************************* +** +** Function OBX_ReadWhoHdr +** +** Description This function is called to get the Who Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadWhoHdr(BT_HDR *p_pkt, UINT8 **p_who, UINT16 *p_len) +{ + return OBX_ReadByteStrHdr(p_pkt, OBX_HI_WHO, p_who, p_len, 0); +} + +/******************************************************************************* +** +** Function OBX_ReadAppParamHdr +** +** Description This function is called to get the Application Parameter Header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadAppParamHdr(BT_HDR *p_pkt, UINT8 *p_tag, UINT8 **p_app_param, UINT8 *p_len, UINT8 next) +{ + BOOLEAN status; + tOBX_TRIPLET triplet[OBX_MAX_TRIPLET]; + UINT8 num = OBX_MAX_TRIPLET; + + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if it returns TRUE. + * leave this unnecessary memset here */ + memset( triplet, 0, sizeof(triplet )); + + status = OBX_ReadTriplet(p_pkt, OBX_HI_APP_PARMS, triplet, &num); + if(status == TRUE) + { + if(next >= num) + { + status = FALSE; + } + else + { + *p_tag = triplet[next].tag; + *p_app_param = triplet[next].p_array; + *p_len = triplet[next].len; + OBX_TRACE_DEBUG3( "OBX_ReadAppParamHdr: next: %d, tag: %x, len: %d", + next, *p_tag, *p_len); + } + } + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadPermissionHdr +** +** Description This function is called to get the Application Parameter Header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadPermissionHdr(BT_HDR *p_pkt, UINT8 *p_user, UINT8 *p_group, UINT8 *p_other) +{ + UINT32 permission; + BOOLEAN status = FALSE; + + if (OBX_Read4ByteHdr(p_pkt, OBX_HI_PERMISSION, &permission)) + { + *p_other = (permission & 0xFF); + permission >>= 8; + *p_group = (permission & 0xFF); + permission >>= 8; + *p_user = (permission & 0xFF); + status = TRUE; + } + + return status; +} + + + + diff --git a/stack/obx/hdrs/obx_dopt.c b/stack/obx/hdrs/obx_dopt.c new file mode 100644 index 0000000..e3d0d27 --- /dev/null +++ b/stack/obx/hdrs/obx_dopt.c @@ -0,0 +1,52 @@ +/***************************************************************************** +** +** Name: obx_dopt.c +** +** File: OBEX Headers Decode Utility functions +** - optional functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "obx_api.h" + + + + + + +/******************************************************************************* +** +** Function OBX_ReadTimeHdr +** +** Description This function is called to get the Time Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadTimeHdr(BT_HDR *p_pkt, UINT8 **p_time, UINT16 *p_len) +{ + return OBX_ReadByteStrHdr(p_pkt, OBX_HI_TIME, p_time, p_len, 0); +} + + +/******************************************************************************* +** +** Function OBX_ReadHttpHdr +** +** Description This function is called to get the HTTP Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadHttpHdr(BT_HDR *p_pkt, UINT8 **p_http, UINT16 *p_len, UINT8 next) +{ + return OBX_ReadByteStrHdr(p_pkt, OBX_HI_HTTP, p_http, p_len, next); +} + diff --git a/stack/obx/hdrs/obx_dunic.c b/stack/obx/hdrs/obx_dunic.c new file mode 100644 index 0000000..c8f6b12 --- /dev/null +++ b/stack/obx/hdrs/obx_dunic.c @@ -0,0 +1,64 @@ +/***************************************************************************** +** +** Name: obx_dunic.c +** +** File: OBEX Headers Decode Utility functions +** - Unicode functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "obx_api.h" + + + + +/******************************************************************************* +** +** Function OBX_ReadNameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadNameHdr(BT_HDR *p_pkt, UINT16 *p_name, UINT16 *p_len) +{ + return OBX_ReadUnicodeHdr(p_pkt, OBX_HI_NAME, p_name, p_len); +} + +/******************************************************************************* +** +** Function OBX_ReadDescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadDescrHdr(BT_HDR *p_pkt, UINT16 *p_descr, UINT16 *p_len) +{ + return OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_descr, p_len); +} + +/******************************************************************************* +** +** Function OBX_ReadDestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadDestNameHdr(BT_HDR *p_pkt, UINT16 *p_dest, UINT16 *p_len) +{ + return OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_dest, p_len); +} diff --git a/stack/obx/hdrs/obx_dutf.c b/stack/obx/hdrs/obx_dutf.c new file mode 100644 index 0000000..a800428 --- /dev/null +++ b/stack/obx/hdrs/obx_dutf.c @@ -0,0 +1,91 @@ +/***************************************************************************** +** +** Name: obx_dutf.c +** +** File: OBEX Headers Decode Utility functions +** - Unicode conversion functions +** +** Copyright (c) 2003-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "obx_api.h" + + +/******************************************************************************* +** +** Function OBX_ReadUtf8NameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. If Name header exists in the given OBEX packet, +** it is converted to UTF8 format and copied into p_name. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadUtf8NameHdr(BT_HDR *p_pkt, UINT8 *p_name, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_NAME, p_unicode, &len); + utfc_16_to_8(p_name, max_len, p_unicode, len); + GKI_freebuf(p_unicode); + return status; +} + + + +/******************************************************************************* +** +** Function OBX_ReadUtf8DescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. If Description header exists in the given +** OBEX packet, it is converted to UTF8 format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadUtf8DescrHdr(BT_HDR *p_pkt, UINT8 *p_descr, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_unicode, &len); + utfc_16_to_8(p_descr, max_len, p_unicode, len); + GKI_freebuf(p_unicode); + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadUtf8DestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadUtf8DestNameHdr(BT_HDR *p_pkt, UINT8 *p_dest, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_unicode, &len); + utfc_16_to_8(p_dest, max_len, p_unicode, len); + GKI_freebuf(p_unicode); + return status; +} + + diff --git a/stack/obx/hdrs/obx_dwchar.c b/stack/obx/hdrs/obx_dwchar.c new file mode 100644 index 0000000..2aa5ebd --- /dev/null +++ b/stack/obx/hdrs/obx_dwchar.c @@ -0,0 +1,91 @@ +/***************************************************************************** +** +** Name: obx_dwchar.c +** +** File: OBEX Headers Decode Utility functions +** - Unicode conversion functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "obx_api.h" + + +/******************************************************************************* +** +** Function OBX_ReadAsciiNameHdr +** +** Description This function is called to get the Name Header in the given +** OBEX packet. If Name header exists in the given OBEX packet, +** it is converted to ASCII format and copied into p_name. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadAsciiNameHdr(BT_HDR *p_pkt, char *p_name, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_NAME, p_unicode, &len); + OBX_WcharToChar(p_name, p_unicode, max_len); + GKI_freebuf(p_unicode); + return status; +} + + + +/******************************************************************************* +** +** Function OBX_ReadAsciiDescrHdr +** +** Description This function is called to get the Description Header in the +** given OBEX packet. If Description header exists in the given +** OBEX packet, it is converted to ASCII format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadAsciiDescrHdr(BT_HDR *p_pkt, char *p_descr, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_unicode, &len); + OBX_WcharToChar(p_descr, p_unicode, max_len); + GKI_freebuf(p_unicode); + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadAsciiDestNameHdr +** +** Description This function is called to get the DestName Header in the +** given OBEX packet. If DestName header exists in the given +** OBEX packet, it is converted to ASCII format and copied into +** p_descr. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadAsciiDestNameHdr(BT_HDR *p_pkt, char *p_dest, UINT16 max_len) +{ + BOOLEAN status; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)((max_len + 1) * 2)); + UINT16 len = max_len; + + status = OBX_ReadUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_unicode, &len); + OBX_WcharToChar(p_dest, p_unicode, max_len); + GKI_freebuf(p_unicode); + return status; +} diff --git a/stack/obx/hdrs/obx_ebtp.c b/stack/obx/hdrs/obx_ebtp.c new file mode 100644 index 0000000..93dd988 --- /dev/null +++ b/stack/obx/hdrs/obx_ebtp.c @@ -0,0 +1,177 @@ +/***************************************************************************** +** +** Name: obx_ebtp.c +** +** File: OBEX Headers Encode Utility functions +** - for current Bluetooth profiles +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "obx_api.h" + + + +/******************************************************************************* +** +** Function OBX_AddTypeHdr +** +** Description This function is called to add an OBEX Type header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddTypeHdr(BT_HDR *p_pkt, char *p_type) +{ + return OBX_AddByteStrHdr(p_pkt, OBX_HI_TYPE, (UINT8*)p_type, (UINT16)(strlen(p_type)+1)); +} + +/******************************************************************************* +** +** Function OBX_AddLengthHdr +** +** Description This function is called to add an OBEX Length header to an OBEX +** packet. The Length header describes the total length in bytes of +** the object. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddLengthHdr(BT_HDR *p_pkt, UINT32 len) +{ + return OBX_Add4ByteHdr(p_pkt, OBX_HI_LENGTH, len); +} + +/******************************************************************************* +** +** Function OBX_AddTargetHdr +** +** Description This function is called to add an OBEX Target header to an OBEX +** packet. This header is most commonly used in Connect packets. +** +** NOTE: The target header must be the first header in an OBEX message. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddTargetHdr(BT_HDR *p_pkt, UINT8 *p_target, UINT16 len) +{ + BOOLEAN add = FALSE; + UINT8 *p; + + if (p_pkt->len == 0) + { + add = TRUE; + } + else if (p_pkt->len == 2) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset; + if ((*p) == OBX_HI_SESSION_SN) + add = TRUE; + + } + /* allow SSN to preceed target header */ + if (add) + return OBX_AddByteStrHdr(p_pkt, OBX_HI_TARGET, p_target, len); + else + { + BT_ERROR_TRACE_1(TRACE_LAYER_OBEX, "Target header must be the first:%d", p_pkt->len) ; + return FALSE; + } +} + +/******************************************************************************* +** +** Function OBX_AddBodyHdr +** +** Description This function is called to add an OBEX body header +** to an OBEX packet. +** +** NOTE: The body header must be the last header in an OBEX message. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddBodyHdr(BT_HDR *p_pkt, UINT8 *p_body, UINT16 len, BOOLEAN end) +{ + UINT8 id = (end)?OBX_HI_BODY_END:OBX_HI_BODY; + return OBX_AddByteStrHdr(p_pkt, id, p_body, len); +} + + +/******************************************************************************* +** +** Function OBX_AddWhoHdr +** +** Description This function is called to add an OBEX Who header to an OBEX +** packet. +** +** Note: Who header is typically used in an OBEX CONNECT response packet +** to indicate the UUID of the service that has accepted the +** directed connection. If the server calls OBX_StartServer() with +** specified target header, this OBEX implementation automatically +** adds this WHO header to the CONNECT response packet. If +** OBX_StartServer() is called with target header length as 0, the +** OBEX API user is responsible to add the WHO header. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddWhoHdr(BT_HDR *p_pkt, UINT8 *p_who, UINT16 len) +{ + return OBX_AddByteStrHdr(p_pkt, OBX_HI_WHO, p_who, len); +} + +/******************************************************************************* +** +** Function OBX_AddAppParamHdr +** +** Description This function is called to add an OBEX Application Parameter +** header to an OBEX packet. This header is used by the application +** layer above OBEX to convey application specific information. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddAppParamHdr(BT_HDR *p_pkt, tOBX_TRIPLET *p_triplet, UINT8 num) +{ + return OBX_AddTriplet(p_pkt, OBX_HI_APP_PARMS, p_triplet, num); +} + +/******************************************************************************* +** +** Function OBX_AddPermissionHdr +** +** Description This function is called to add an OBEX Permission header to an OBEX +** packet. +** bit 0 is set for read permission +** bit 1 is set for write permission +** bit 2 is set for delete permission +** bit 7 is set for modify permission +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddPermissionHdr(BT_HDR *p_pkt, UINT8 user, UINT8 group, UINT8 other) +{ + UINT32 permission = user; + permission <<= 8; + permission |= group; + permission <<= 8; + permission |= other; + return OBX_Add4ByteHdr(p_pkt, OBX_HI_PERMISSION, permission); +} + + diff --git a/stack/obx/hdrs/obx_eopt.c b/stack/obx/hdrs/obx_eopt.c new file mode 100644 index 0000000..e0b0487 --- /dev/null +++ b/stack/obx/hdrs/obx_eopt.c @@ -0,0 +1,51 @@ +/***************************************************************************** +** +** Name: obx_eopt.c +** +** File: OBEX Headers Encode Utility functions +** - optional functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "obx_api.h" + + + +/******************************************************************************* +** +** Function OBX_AddTimeHdr +** +** Description This function is called to add an OBEX Time header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddTimeHdr(BT_HDR *p_pkt, char *p_time) +{ + return OBX_AddByteStrHdr(p_pkt, OBX_HI_TIME, (UINT8 *)p_time, (UINT16)strlen(p_time)); +} + + +/******************************************************************************* +** +** Function OBX_AddHttpHdr +** +** Description This function is called to add an OBEX HTTP header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddHttpHdr(BT_HDR *p_pkt, UINT8 *p_http, UINT16 len) +{ + return OBX_AddByteStrHdr(p_pkt, OBX_HI_HTTP, p_http, len); +} + + diff --git a/stack/obx/hdrs/obx_eunic.c b/stack/obx/hdrs/obx_eunic.c new file mode 100644 index 0000000..6b5cb29 --- /dev/null +++ b/stack/obx/hdrs/obx_eunic.c @@ -0,0 +1,64 @@ +/***************************************************************************** +** +** Name: obx_eunic.c +** +** File: OBEX Headers Encode Utility functions +** - Unicode functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "obx_api.h" + + +/******************************************************************************* +** +** Function OBX_AddNameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddNameHdr(BT_HDR *p_pkt, UINT16 *p_name, UINT16 len) +{ + return OBX_AddUnicodeHdr(p_pkt, OBX_HI_NAME, p_name, len); +} + +/******************************************************************************* +** +** Function OBX_AddDescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddDescrHdr(BT_HDR *p_pkt, UINT16 *p_descr, UINT16 len) +{ + return OBX_AddUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_descr, len); +} + +/******************************************************************************* +** +** Function OBX_AddDestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddDestNameHdr(BT_HDR *p_pkt, UINT16 *p_dest, UINT16 len) +{ + return OBX_AddUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_dest, len); +} + + diff --git a/stack/obx/hdrs/obx_eutf.c b/stack/obx/hdrs/obx_eutf.c new file mode 100644 index 0000000..b298500 --- /dev/null +++ b/stack/obx/hdrs/obx_eutf.c @@ -0,0 +1,111 @@ +/***************************************************************************** +** +** Name: obx_eutf.c +** +** File: OBEX Headers Encode Utility functions +** - Unicode conversion functions +** +** Copyright (c) 2003-2005, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "gki.h" +#include "obx_api.h" + + +/******************************************************************************* +** +** Function OBX_AddUtf8NameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddUtf8NameHdr(BT_HDR *p_pkt, UINT8 *p_name) +{ + BOOLEAN status = FALSE; + UINT16 utf16_len = 0; + UINT16 *p_utf16 = NULL; + + if (p_name) + { + utf16_len = strlen((char *)p_name) * 2 + 2; + p_utf16 = (UINT16 *)GKI_getbuf(utf16_len); + } + + if (p_utf16) + utf16_len = utfc_8_to_16(p_utf16, utf16_len, p_name); + else + utf16_len = 0; + + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_NAME, p_utf16, utf16_len); + + if (p_utf16) + GKI_freebuf(p_utf16); + + return status; +} + +/******************************************************************************* +** +** Function OBX_AddUtf8DescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddUtf8DescrHdr(BT_HDR *p_pkt, UINT8 *p_descr) +{ + BOOLEAN status = FALSE; + UINT16 utf16_len; + UINT16 *p_utf16; + + utf16_len = strlen((char *)p_descr) * 2 + 2; + p_utf16 = (UINT16 *)GKI_getbuf(utf16_len); + if(p_utf16) + { + utf16_len = utfc_8_to_16(p_utf16, utf16_len, p_descr); + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_utf16, utf16_len); + GKI_freebuf(p_utf16); + } + return status; +} + +/******************************************************************************* +** +** Function OBX_AddUtf8DestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddUtf8DestNameHdr(BT_HDR *p_pkt, UINT8 *p_dest) +{ + BOOLEAN status = FALSE; + UINT16 utf16_len; + UINT16 *p_utf16; + + utf16_len = strlen((char *)p_dest) * 2 + 2; + p_utf16 = (UINT16 *)GKI_getbuf(utf16_len); + if(p_utf16) + { + utf16_len = utfc_8_to_16(p_utf16, utf16_len, p_dest); + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_utf16, utf16_len); + GKI_freebuf(p_utf16); + } + return status; +} + + + diff --git a/stack/obx/hdrs/obx_ewchar.c b/stack/obx/hdrs/obx_ewchar.c new file mode 100644 index 0000000..2025baa --- /dev/null +++ b/stack/obx/hdrs/obx_ewchar.c @@ -0,0 +1,100 @@ +/***************************************************************************** +** +** Name: obx_ewchar.c +** +** File: OBEX Headers Encode Utility functions +** - Unicode conversion functions +** +** Copyright (c) 2003-2005, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "gki.h" +#include "obx_api.h" + +/******************************************************************************* +** +** Function OBX_AddAsciiNameHdr +** +** Description This function is called to add an OBEX Name header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddAsciiNameHdr(BT_HDR *p_pkt, char *p_name) +{ + BOOLEAN status; + UINT16 len = 0; + UINT16 *p_unicode = NULL; + + if(p_name) + { + len = strlen(p_name) + 1; + p_unicode = (UINT16 *)GKI_getbuf((UINT16)(len*2)); + } + + if(p_unicode) + len = OBX_CharToWchar(p_unicode, p_name, len); + else + len = 0; + + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_NAME, p_unicode, len); + + if(p_unicode) + GKI_freebuf(p_unicode); + return status; +} + + +/******************************************************************************* +** +** Function OBX_AddAsciiDescrHdr +** +** Description This function is called to add an OBEX Description header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddAsciiDescrHdr(BT_HDR *p_pkt, char *p_descr) +{ + BOOLEAN status; + UINT16 len = strlen(p_descr) + 1; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)(len*2)); + + len = OBX_CharToWchar(p_unicode, p_descr, len); + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_DESCRIPTION, p_unicode, len); + GKI_freebuf(p_unicode); + return status; +} + +/******************************************************************************* +** +** Function OBX_AddAsciiDestNameHdr +** +** Description This function is called to add an OBEX DestName header +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddAsciiDestNameHdr(BT_HDR *p_pkt, char *p_dest) +{ + BOOLEAN status; + UINT16 len = strlen(p_dest) + 1; + UINT16 *p_unicode = (UINT16 *)GKI_getbuf((UINT16)(len*2)); + + len = OBX_CharToWchar(p_unicode, p_dest, len); + status = OBX_AddUnicodeHdr(p_pkt, OBX_HI_DEST_NAME, p_unicode, len); + GKI_freebuf(p_unicode); + return status; +} + + + diff --git a/stack/obx/hdrs/obx_gen.c b/stack/obx/hdrs/obx_gen.c new file mode 100644 index 0000000..9b25e04 --- /dev/null +++ b/stack/obx/hdrs/obx_gen.c @@ -0,0 +1,896 @@ +/***************************************************************************** +** +** Name: obx_gen.c +** +** File: OBEX Headers Utility functions +** - common/generic functions +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "wcassert.h" +#include "obx_api.h" +#include "gki.h" +#include "obx_int.h" + +const UINT8 obx_hdr_start_offset[] = +{ + OBX_CONN_HDRS_OFFSET, /* OBX_CONNECT_REQ_EVT */ + OBX_SESS_HDRS_OFFSET, /* OBX_SESSION_REQ_EVT */ + OBX_DISCON_HDRS_OFFSET, /* OBX_DISCONNECT_REQ_EVT */ + OBX_PUT_HDRS_OFFSET, /* OBX_PUT_REQ_EVT */ + OBX_GET_HDRS_OFFSET, /* OBX_GET_REQ_EVT */ + OBX_SETPATH_REQ_HDRS_OFFSET,/* OBX_SETPATH_REQ_EVT */ + OBX_ABORT_HDRS_OFFSET, /* OBX_ABORT_REQ_EVT */ + OBX_ACTION_HDRS_OFFSET, /* OBX_ACTION_REQ_EVT */ + OBX_CONN_HDRS_OFFSET, /* OBX_CONNECT_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_SESSION_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_DISCONNECT_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_PUT_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_GET_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_SETPATH_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET, /* OBX_ABORT_RSP_EVT */ + OBX_RESPONSE_HDRS_OFFSET /* OBX_ACTION_RSP_EVT */ +}; + +/******************************************************************************* +** +** Function obx_access_rsp_code +** +** Description This function is used to read/change response code +** +** Returns void. +** +*******************************************************************************/ +void obx_access_rsp_code(BT_HDR *p_pkt, UINT8 *p_rsp_code) +{ + UINT8 *p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + if(*p_rsp_code == OBX_RSP_DEFAULT) + *p_rsp_code = *p; + else + *p = *p_rsp_code; +} + +/******************************************************************************* +** +** Function obx_adjust_packet_len +** +** Description Adjust the packet length in the OBEX packet +** +** Returns void. +** +*******************************************************************************/ +void obx_adjust_packet_len(BT_HDR *p_pkt) +{ + UINT8 *p = (UINT8 *)(p_pkt + 1) + p_pkt->offset + 1; + UINT16_TO_BE_STREAM(p, p_pkt->len); +} + +/******************************************************************************* +** +** Function obx_read_header_len +** +** Description ph is the beginning of an OBEX header +** +** Returns total length of the header +** +*******************************************************************************/ +UINT16 obx_read_header_len(UINT8 *ph) +{ + UINT16 len = 0; + + /* + OBX_TRACE_DEBUG1( "obx_read_header_len: 0x%x", *ph); + */ + switch(*ph&OBX_HI_TYPE_MASK) + { + case OBX_HI_TYPE_BYTE: + len = 2; + break; + case OBX_HI_TYPE_INT: + len = 5; + break; + case OBX_HI_TYPE_ARRAY: + case OBX_HI_TYPE_UNIC: + ph++; + BE_STREAM_TO_UINT16(len, ph); + break; + } + /* + OBX_TRACE_DEBUG1( "len:%d", len); + */ + return len; +} + +/******************************************************************************* +** +** Function obx_dup_pkt +** +** Description This function duplicate the OBEX message +** +** Returns BT_HDR *. +** +*******************************************************************************/ +BT_HDR * obx_dup_pkt(BT_HDR *p_pkt) +{ + BT_HDR *p_new; + UINT16 size = p_pkt->len + p_pkt->offset + BT_HDR_SIZE; + + if (size < GKI_MAX_BUF_SIZE) + { + /* Use the largest general pool to allow challenge tags appendage */ + p_new = (BT_HDR *)GKI_getbuf(GKI_MAX_BUF_SIZE); + } + else + { + p_new = (BT_HDR *) GKI_getpoolbuf(OBX_LRG_DATA_POOL_ID); + } + + if (p_new) + memcpy(p_new, p_pkt, size ); + + return p_new; +} + + +/******************************************************************************* +** +** Function OBX_HdrInit +** +** Description This function is called to initialize an OBEX packet. This +** function takes a GKI buffer and sets the offset in BT_HDR as +** OBX_HDR_OFFSET, the len as 0. The layer_specific is set to the +** length still available. This function compares the given +** (pkt_size - BT_HDR_SIZE) with the peer MTU to get the lesser +** of the two and set the layer_specific to +** (lesser_size - OBX_HDR_OFFSET). +** If composing a header for the CONNECT request (there is no +** client handle yet), use OBX_HANDLE_NULL as the handle. +** +** If the pkt_size is larger than the largest public pool size, +** GKI_MAX_BUF_SIZE, then an attempt to grab a buffer from the reserved OBX +** data pool will be made. +** Returns BT_HDR *. +** +*******************************************************************************/ +BT_HDR *OBX_HdrInit(tOBX_HANDLE handle, UINT16 pkt_size) +{ + UINT16 mtu = OBX_HandleToMtu(handle); + BT_HDR *p_pkt = NULL; + UINT16 buf_size; +#if (BT_USE_TRACES == TRUE) + UINT16 req_size = pkt_size; +#endif + + WC_ASSERT(pkt_size > (BT_HDR_SIZE + OBX_HDR_OFFSET)); + + pkt_size -= BT_HDR_SIZE; + if(pkt_size > mtu ) + pkt_size = mtu; + pkt_size += (BT_HDR_SIZE + OBX_HDR_OFFSET); + + OBX_TRACE_DEBUG4( "OBX_HdrInit: checking req_size %d, pkt_size:%d, max:%d, offset:%d", + req_size, pkt_size, GKI_MAX_BUF_SIZE, OBX_HDR_OFFSET); + /* See if packet will fit in regular public pool */ + if ((pkt_size) < GKI_MAX_BUF_SIZE) + { + p_pkt = (BT_HDR *) GKI_getbuf(pkt_size); + } + else /* Must use the reserved OBX buffer pool */ + { + p_pkt = (BT_HDR *) GKI_getpoolbuf(OBX_LRG_DATA_POOL_ID); + if (!p_pkt) + { + OBX_TRACE_DEBUG1( "Out of Large buffers. Trying pkt_size:%d", GKI_MAX_BUF_SIZE); + p_pkt = (BT_HDR *) GKI_getbuf(GKI_MAX_BUF_SIZE); + } + } + + if(p_pkt) + { + buf_size = GKI_get_buf_size(p_pkt); + buf_size -= BT_HDR_SIZE; + if(buf_size > mtu) + buf_size = mtu; + + OBX_TRACE_DEBUG4( "OBX_HdrInit: req_size %d, pkt_size = %d, gki_size %d, buf_size %d", + req_size, pkt_size, GKI_get_buf_size(p_pkt), buf_size); + + p_pkt->offset = OBX_HDR_OFFSET; + p_pkt->len = 0; + p_pkt->event = 0; + + /* layer specific contains remaining space in packet */ + p_pkt->layer_specific = buf_size - OBX_HDR_OFFSET ; + p_pkt->layer_specific -= 2; + + OBX_TRACE_DEBUG2( "buf size: %d, ls:%d", buf_size, p_pkt->layer_specific); + } + else + { + OBX_TRACE_ERROR1("OBX_HdrInit: No buffers for size (%d)", pkt_size); + } + + return p_pkt; +} + + +/******************************************************************************* +** +** Function OBX_Add1ByteHdr +** +** Description This function is called to add a header with type as UINT8 +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_Add1ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT8 data) +{ + UINT8 *p; + BOOLEAN status = FALSE; + UINT16 size = 2; /* total length added by this header - 1/hi & 1/hv */ + + if(p_pkt) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + /* verify that the HI is of correct type and the remaining length in the packet is good */ + if( ((id & OBX_HI_TYPE_MASK) == OBX_HI_TYPE_BYTE) && (p_pkt->layer_specific >= size) ) + { + *p++ = id; + *p++ = data; + + p_pkt->len += size; + p_pkt->layer_specific -= size; + status = TRUE; + } + } + + return status; +} + +/******************************************************************************* +** +** Function OBX_Add4ByteHdr +** +** Description This function is called to add a header with type as UINT32 +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_Add4ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT32 data) +{ + UINT8 *p; + BOOLEAN status = FALSE; + UINT16 size = 5; /* total length added by this header - 1/hi & 4/hv */ + + if(p_pkt) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + /* verify that the HI is of correct type and the remaining length in the packet is good */ + if( ((id & OBX_HI_TYPE_MASK) == OBX_HI_TYPE_INT) && (p_pkt->layer_specific >= size) ) + { + *p++ = id; + UINT32_TO_BE_STREAM(p, data); + + p_pkt->len += size; + p_pkt->layer_specific -= size; + status = TRUE; + } + } + + return status; +} + +/******************************************************************************* +** +** Function OBX_AddByteStrStart +** +** Description This function is called to get the address to the beginning of +** the byte sequence for an OBEX header in an OBEX packet. +** +** Returns The address to add the byte sequence. +** +*******************************************************************************/ +UINT8 *OBX_AddByteStrStart(BT_HDR *p_pkt, UINT16 *p_len) +{ + UINT8 *p = (UINT8 *)(p_pkt + 1) + p_pkt->offset + p_pkt->len + 3; + + WC_ASSERT(p_len); + + if(*p_len > (p_pkt->layer_specific - 3) || *p_len == 0) + *p_len = p_pkt->layer_specific - 3; + return p; +} + +/******************************************************************************* +** +** Function OBX_AddByteStrHdr +** +** Description This function is called to add a header with type as byte sequence +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddByteStrHdr(BT_HDR *p_pkt, UINT8 id, UINT8 *p_data, UINT16 len) +{ + UINT8 *p; + BOOLEAN status = FALSE; + UINT16 size = (len+3); /* total length added by this header - 1/hi & len+2/hv */ + + if(p_pkt) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + /* verify that the HI is of correct type and the remaining length in the packet is good */ + if( ((id & OBX_HI_TYPE_MASK) == OBX_HI_TYPE_ARRAY) && (p_pkt->layer_specific >= size) ) + { + *p++ = id; + UINT16_TO_BE_STREAM(p, size); + if(p_data) + memcpy(p, p_data, len); + + p_pkt->len += size; + p_pkt->layer_specific -= size; + status = TRUE; + } + } + + return status; +} + +/******************************************************************************* +** +** Function OBX_AddUnicodeHdr +** +** Description This function is called to add a header with type as Unicode string +** to an OBEX packet. +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddUnicodeHdr(BT_HDR *p_pkt, UINT8 id, UINT16 *p_data, UINT16 len) +{ + UINT8 *p; + BOOLEAN status = FALSE; + UINT16 size, xx; + + if(p_pkt) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + size = (len*OBX_UNICODE_SIZE + 3); /* total length added by this header - 1/hi & len*OBX_UNICODE_SIZE+2/hv */ + OBX_TRACE_DEBUG4( "OBX_AddUnicodeHdr len: %d, size: %d, left: %d, id: 0x%x", + len, size, p_pkt->layer_specific, id ); + + /* verify that the HI is of correct type and the remaining length in the packet is good */ + if( ((id & OBX_HI_TYPE_MASK) == OBX_HI_TYPE_UNIC) && (p_pkt->layer_specific >= size) ) + { + *p++ = id; + UINT16_TO_BE_STREAM(p, size); + for(xx=0; xx<len; xx++) + { + UINT16_TO_BE_STREAM(p, *p_data); + p_data++; + } + + p_pkt->len += size; + p_pkt->layer_specific -= size; + status = TRUE; + } + } + + return status; +} + +/* Alternate Body header functions: for non-blocking scenario */ +/******************************************************************************* +** +** Function OBX_AddBodyStart +** +** Description This function is called to get the address to the beginning of +** the byte sequence for an OBEX body header in an OBEX packet. +** +** Returns The address to add body content. +** +*******************************************************************************/ +UINT8 *OBX_AddBodyStart(BT_HDR *p_pkt, UINT16 *p_len) +{ + UINT8 *p = (UINT8 *)(p_pkt + 1) + p_pkt->offset + p_pkt->len + 3; + + WC_ASSERT(p_len); + + if(*p_len > (p_pkt->layer_specific - 3) || *p_len == 0) + *p_len = p_pkt->layer_specific - 3; + return p; +} + +/******************************************************************************* +** +** Function OBX_AddBodyEnd +** +** Description This function is called to add the HI and the length of HV of an +** OBEX body header to an OBEX packet. If end is TRUE, HI is +** OBX_HI_BODY_END. If FALSE, HI is OBX_HI_BODY. It is assumed that +** the actual value of the body has been copied into the OBEX packet. +** +** Returns void +** +*******************************************************************************/ +void OBX_AddBodyEnd(BT_HDR *p_pkt, UINT8 *p, UINT16 len, BOOLEAN end) +{ + UINT8 id = (end)?OBX_HI_BODY_END:OBX_HI_BODY; + UINT8 *pb = (UINT8 *)(p_pkt + 1) + p_pkt->offset + p_pkt->len; + *pb++ = id; + len += 3; /* 1/hi, 2/hv_len */ + UINT16_TO_BE_STREAM(pb, len); + p_pkt->layer_specific -= len; + p_pkt->len += len; +} + +/******************************************************************************* +** +** Function OBX_AddTriplet +** +** Description This function is called to add a header with type as byte sequence +** to an OBEX packet. +** +** Note: The byte sequence uses a Tag-Length-Value encoding scheme +** These headers include: Application Parameters header +** Authenticate Challenge header +** Authenticate Response header +** +** Returns TRUE, if the header is added successfully. +** FALSE, if the operation failed. p_pkt is not altered. +** +*******************************************************************************/ +BOOLEAN OBX_AddTriplet(BT_HDR *p_pkt, UINT8 id, tOBX_TRIPLET *p_triplet, UINT8 num) +{ + UINT8 *p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + BOOLEAN status = FALSE; + UINT16 size = 3;/* 1/hi & len+2/hv */ + UINT8 xx; + + /* calculate the total length added by this header */ + for(xx=0; xx< num; xx++) + size += (p_triplet[xx].len + 2); + + /* verify that the HI is of correct type and the remaining length in the packet is good */ + if( ((id & OBX_HI_TYPE_MASK) == OBX_HI_TYPE_ARRAY) && (p_pkt->layer_specific >= size) ) + { + *p++ = id; + UINT16_TO_BE_STREAM(p, size); + for(xx=0; xx< num; xx++) + { + *p++ = p_triplet[xx].tag; + *p++ = p_triplet[xx].len; + memcpy(p, p_triplet[xx].p_array, p_triplet[xx].len); + p += p_triplet[xx].len; + } + p_pkt->len += size; + p_pkt->layer_specific -= size; + status = TRUE; + } + + return status; +} + + +/******************************************************************************* +** +** Function OBX_CheckNext +** +** Description This function is called to check if the given OBEX packet +** contains the specified header. +** +** Returns NULL, if the header is not in the OBEX packet. +** The pointer to the specified header beginning from HI. +** +*******************************************************************************/ +UINT8 * OBX_CheckNext(BT_HDR *p_pkt, UINT8 *p_start, UINT8 id) +{ + UINT8 *p; + UINT8 *p_res = NULL; + UINT16 len, start, skip; + int remain; + + if(p_pkt != NULL && p_start != NULL) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset; + if(p_pkt->event <= OBX_MAX_OFFSET_IND) + { + start = obx_hdr_start_offset[p_pkt->event-1]; + p++; + BE_STREAM_TO_UINT16(len, p); + remain = len - start; + p = p - 3 + start; + + while(remain >0) + { + if(*p != id || p < p_start) + { + skip = obx_read_header_len(p); + p += skip; + /* Just in case this is a bad packet, make sure that remain is >= 0 */ + if(skip && (remain > skip)) + remain -= skip; + else + remain = 0; + } + else + { + p_res = p; + break; + } + } + } + } + + if (p_pkt) + { + OBX_TRACE_DEBUG2( "OBX_CheckNext: remain: %d len:%d", remain, p_pkt->len); + } + + return p_res; +} + + +/******************************************************************************* +** +** Function OBX_CheckHdr +** +** Description This function is called to check if the given OBEX packet +** contains the specified header. +** +** Returns NULL, if the header is not in the OBEX packet. +** The pointer to the specified header beginning from HI. +** +*******************************************************************************/ +UINT8 * OBX_CheckHdr(BT_HDR *p_pkt, UINT8 id) +{ + UINT8 *p; + UINT8 *p_res = NULL; + UINT16 len, start, skip; + int remain; + + if(p_pkt != NULL) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset; + if(p_pkt->event <= OBX_MAX_OFFSET_IND) + { + start = obx_hdr_start_offset[p_pkt->event-1]; + p++; + BE_STREAM_TO_UINT16(len, p); + remain = len - start; + p = p - 3 + start; + + while(remain >0) + { + if(*p != id) + { + skip = obx_read_header_len(p); + p += skip; + /* Just in case this is a bad packet, make sure that remain is >= 0 */ + if(skip && (remain > skip)) + remain -= skip; + else + remain = 0; + } + else + { + p_res = p; + break; + } + } + } + } + + return p_res; +} + +/******************************************************************************* +** +** Function OBX_ReadNumHdrs +** +** Description This function is called to check the number of headers in the +** given OBEX packet +** +** Returns number of headers. +** +*******************************************************************************/ +UINT8 OBX_ReadNumHdrs(BT_HDR *p_pkt, UINT8 *p_num_body) +{ + UINT8 num_hdrs = 0, num_body = 0; + UINT8 *p; + UINT16 len, start, skip; + int remain = 0; + + if(p_pkt != NULL) + { + p = (UINT8 *)(p_pkt+1)+p_pkt->offset; + if(p_pkt->event == 0) + { + /* GKI buffer just went through OBX_HdrInit; not processed by the state machine yet */ + remain = len = p_pkt->len; + } + else if(p_pkt->event <= OBX_MAX_OFFSET_IND) + { + start = obx_hdr_start_offset[p_pkt->event-1]; + p++; + BE_STREAM_TO_UINT16(len, p); + remain = len - start; + p = p - 3 + start; + } + + while(remain >0) + { + num_hdrs++; + if(*p == OBX_HI_BODY || *p == OBX_HI_BODY_END) + num_body++; + + skip = obx_read_header_len(p); + p += skip; + /* Just in case this is a bad packet, make sure that remain is >= 0 */ + if(skip && (remain > skip)) + remain -= skip; + else + remain = 0; + + } + } + if (p_num_body) + *p_num_body = num_body; + return num_hdrs; +} + +/******************************************************************************* +** +** Function OBX_ReadHdrLen +** +** Description This function is called to check the length of the specified +** header in the given OBEX packet. +** +** Returns OBX_INVALID_HDR_LEN, if the header is not in the OBEX packet. +** Otherwise the actual length of the header. +** +*******************************************************************************/ +UINT16 OBX_ReadHdrLen(BT_HDR *p_pkt, UINT8 id) +{ + UINT8 *p; + UINT16 len = OBX_INVALID_HDR_LEN; + + if( (p = OBX_CheckHdr(p_pkt, id)) != NULL) + len = obx_read_header_len(p); + + return len; +} + +/******************************************************************************* +** +** Function OBX_Read1ByteHdr +** +** Description This function is called to get the UINT8 HV of the given HI +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_Read1ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT8 *p_data) +{ + BOOLEAN status = FALSE; + UINT8 *p_start = OBX_CheckHdr(p_pkt, id); + + if(p_start) + { + *p_data = *(++p_start); + status = TRUE; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_Read4ByteHdr +** +** Description This function is called to get the UINT32 HV of the given HI +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_Read4ByteHdr(BT_HDR *p_pkt, UINT8 id, UINT32 *p_data) +{ + BOOLEAN status = FALSE; + UINT8 *p_start = OBX_CheckHdr(p_pkt, id); + + if(p_start) + { + p_start++; + BE_STREAM_TO_UINT32(*p_data, p_start); + status = TRUE; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadByteStrHdr +** +** Description This function is called to get the byte sequence HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadByteStrHdr(BT_HDR *p_pkt, UINT8 id, UINT8 **p_data, UINT16 *p_len, UINT8 next) +{ + BOOLEAN status = FALSE; + UINT8 *p_start = OBX_CheckHdr(p_pkt, id); + + if(p_start) + { + next += 1; + while(next && (id == *p_start++)) + { + next--; + BE_STREAM_TO_UINT16(*p_len, p_start); + if(next == 0) + { + status = TRUE; + *p_len -= 3; /* get rid of hi and hv_len */ + *p_data = p_start; + } + else + p_start = p_start + *p_len - 3; + } + } + else + { + *p_len = 0; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadUnicodeHdr +** +** Description This function is called to get the Unicode HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadUnicodeHdr(BT_HDR *p_pkt, UINT8 id, UINT16 *p_data, UINT16 *p_len) +{ + BOOLEAN status = FALSE; + UINT16 len, xx, max_len; + UINT8 *p_start = OBX_CheckHdr(p_pkt, id); + + if(p_start) + { + max_len = *p_len; + p_start++; /* 1/hi*/ + BE_STREAM_TO_UINT16(len, p_start); + len -= 3; /* 1/hi, 2/hv_len */ + len /= OBX_UNICODE_SIZE; /* size in UINT16 */ + /* only conver the provided size */ + if( len > max_len) + len = max_len; + for(xx=0; xx<len; xx++) + { + BE_STREAM_TO_UINT16(*p_data, p_start); + p_data++; + } + *p_len = len; + status = TRUE; + max_len -= len; + while ( (p_start = OBX_CheckNext(p_pkt, p_start, id)) != NULL && (max_len > 0)) + { + p_start++; /* 1/hi*/ + BE_STREAM_TO_UINT16(len, p_start); + len -= 3; /* 1/hi, 2/hv_len */ + len /= OBX_UNICODE_SIZE; /* size in UINT16 */ + /* only conver the provided size */ + if( len > max_len) + len = max_len; + for(xx=0; xx<len; xx++) + { + BE_STREAM_TO_UINT16(*p_data, p_start); + p_data++; + } + *p_len += len; + max_len -= len; + } + } + else + { + *p_len = 0; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadTriplet +** +** Description This function is called to get the Triplet HV of the given +** HI in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadTriplet(BT_HDR *p_pkt, UINT8 id, tOBX_TRIPLET *p_triplet, UINT8 *p_num) +{ + BOOLEAN status = FALSE; + UINT8 *p_start = OBX_CheckHdr(p_pkt, id); + UINT16 len; + UINT8 count = 0; + + if(p_start) + { + p_start++; /* 1/hi*/ + BE_STREAM_TO_UINT16(len, p_start); + len -= 3; /* 1/hi, 2/hv_len */ + while(len && *p_num > count) + { + p_triplet[count].tag = *p_start++; + p_triplet[count].len = *p_start++; + OBX_TRACE_DEBUG3( "OBX_ReadTriplet: count: %d, tag: %x, len: %d", + count, p_triplet[count].tag, p_triplet[count].len); + p_triplet[count].p_array = p_start; + p_start += p_triplet[count].len; + if(len > (p_triplet[count].len + 2) ) + len -= (p_triplet[count].len + 2); + else + len = 0; + count++; + } + status = TRUE; + } + *p_num = count; + return status; +} + +/******************************************************************************* +** +** Function OBX_ReadActionIdHdr +** +** Description This function is called to get the HV of the Action ID header +** in the given OBEX packet. +** +** Returns TRUE, if the header is in the OBEX packet. +** FALSE, otherwise. +** +*******************************************************************************/ +BOOLEAN OBX_ReadActionIdHdr(BT_HDR *p_pkt, UINT8 *p_data) +{ + BOOLEAN status = FALSE; + UINT8 *p_start = OBX_CheckHdr(p_pkt, OBX_HI_ACTION_ID); + + if(p_start) + { + p_start++; + /* check for valid values: 0-2 */ + /* do not allow 0x80 - 0xFF (vendor extention) for now. */ + if (*p_start <= OBX_ACT_PERMISSION) + { + *p_data = *(p_start); + status = TRUE; + } + } + return status; +} diff --git a/stack/obx/hdrs/obx_wchar.c b/stack/obx/hdrs/obx_wchar.c new file mode 100644 index 0000000..5364210 --- /dev/null +++ b/stack/obx/hdrs/obx_wchar.c @@ -0,0 +1,84 @@ +/***************************************************************************** +** +** Name: obx_wchar.c +** +** File: OBEX Headers Encode conversion Utility functions +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "gki.h" +#include "obx_api.h" + +/******************************************************************************* +** +** Function OBX_CharToWchar +** +** Description This function is called to convert ASCII to Unicode (UINT16). +** +** Returns the length. +** +*******************************************************************************/ +UINT16 OBX_CharToWchar (UINT16 *w_str, char* a_str, UINT16 w_size) +{ + UINT16 result = 0; + int size = w_size; + + if (a_str == NULL || w_str == NULL) + return 0; + + while (size > 0 && *a_str != '\0') + { + w_str[result++] = (UINT16) *a_str; + a_str++; + size--; + } + + if (size > 0) + { + w_str[result] = 0; + } + + return (result+1); +} + +/******************************************************************************* +** +** Function OBX_WcharToChar +** +** Description This function is called to convert Unicode (UINT16) to ASCII. +** +** Returns void. +** +*******************************************************************************/ +void OBX_WcharToChar (char *a_str, UINT16* w_str, UINT16 a_size) +{ + UINT16 result = 0; + int size = a_size; + + if (w_str == NULL || a_str == NULL) + return; + + while (size > 0 && *w_str != 0) + { + if ((*w_str & ~0xff) != 0) + { + result = 0; + break; + } + + a_str[result++] = (char) *w_str; + ++(w_str); + --size; + } + + if(size) + a_str[result] = 0; + + return; + + +} + diff --git a/stack/obx/hdrs/utfc.c b/stack/obx/hdrs/utfc.c new file mode 100644 index 0000000..d59733d --- /dev/null +++ b/stack/obx/hdrs/utfc.c @@ -0,0 +1,245 @@ +/***************************************************************************** +** +** Name: utfc.c +** +** Description: UTF conversion utilities. +** +** Copyright (c) 2003-2004, WIDCOMM Inc., All Rights Reserved. +** +*****************************************************************************/ + +#include <string.h> +#include "bt_target.h" +#include "utfc.h" + +/* Based on code from Unicode, Inc: + * + * Copyright 2001-2003 Unicode, Inc. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/******************************************************************************* +** Constants +*******************************************************************************/ + +#define UTFC_8_MASK 0xBF +#define UTFC_8_MARK 0x80 + +#define UTFC_SUR_HIGH_START 0xD800 +#define UTFC_SUR_HIGH_END 0xDBFF +#define UTFC_SUR_LOW_START 0xDC00 +#define UTFC_SUR_LOW_END 0xDFFF + +/* Lookup table for length of UTF-8 byte sequence based on upper four bits +** of first byte. Illegal values give length zero. +*/ +static const UINT8 utfc_8_seq_len[] = {1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 2, 2, 3, 4}; + +/* Magic values subtracted from a buffer value during UTF-8 conversion. +** This table contains as many values as there might be trailing bytes +** in a UTF-8 sequence. +**/ +static const UINT32 utfc_8_offset[] = {0x00000000, 0x00003080, + 0x000E2080, 0x03C82080}; + +static const UINT8 utfc_8_first_byte[] = {0x00, 0x00, 0xC0, 0xE0, 0xF0}; + +/******************************************************************************* +** +** Function utfc_16_to_8 +** +** Description Convert a UTF-16 array to a null-terminated UTF-8 string. +** Illegal characters are skipped. +** +** Returns Length of UTF-8 string in bytes. +** +*******************************************************************************/ +UINT16 utfc_16_to_8(UINT8 *p_utf8, UINT16 utf8_len, UINT16 *p_utf16, UINT16 utf16_len) +{ + UINT32 ch, ch2; + UINT16 len = 0; + UINT8 seq_len; + + /* sanity check destination buffer len */ + if (utf8_len == 0) + { + /* set null */ + *p_utf8 = 0; + return len; + } + + /* save space for null */ + utf8_len--; + + while (utf16_len-- > 0) + { + ch = (UINT32) *p_utf16++; + + /* if we have a surrogate pair, convert to UTF-32 first */ + if (ch >= UTFC_SUR_HIGH_START && ch <= UTFC_SUR_HIGH_END) + { + /* if not enough characters we're done */ + if (utf16_len == 0) + { + break; + } + + /* get next character */ + ch2 = *p_utf16++; + utf16_len--; + + /* if it's a low surrogate, convert to UTF-32 */ + if (ch2 >= UTFC_SUR_LOW_START && ch2 <= UTFC_SUR_LOW_END) + { + ch = ((ch - UTFC_SUR_HIGH_START) << 10) + + (ch2 - UTFC_SUR_LOW_START) + 0x00010000; + } + else + { + /* illegal UTF-16 sequence, skip it */ + continue; + } + } + + /* Figure out how many bytes the result will require */ + if (ch < 0x00000080) + seq_len = 1; + else if (ch < 0x00000800) + seq_len = 2; + else if (ch < 0x00010000) + seq_len = 3; + else + seq_len = 4; + + /* if sequence doesn't fit we're done */ + if (utf8_len < len + seq_len) + { + break; + } + + /* build UTF-8 sequence */ + switch (seq_len) + { /* note: everything falls through. */ + case 4: p_utf8[3] = (UINT8) ((ch | UTFC_8_MARK) & UTFC_8_MASK); ch >>= 6; + case 3: p_utf8[2] = (UINT8) ((ch | UTFC_8_MARK) & UTFC_8_MASK); ch >>= 6; + case 2: p_utf8[1] = (UINT8) ((ch | UTFC_8_MARK) & UTFC_8_MASK); ch >>= 6; + case 1: p_utf8[0] = (UINT8) (ch | utfc_8_first_byte[seq_len]); + } + + /* converted value is a null we're done */ + if (*p_utf8 == 0) + { + break; + } + + p_utf8 += seq_len; + len += seq_len; + } + + /* set null */ + *p_utf8 = 0; + + return len; +} + +/******************************************************************************* +** +** Function utfc_8_to_16 +** +** Description Convert a null-terminated UTF-8 string to a UTF-16 array. +** Illegal characters are skipped. The UTF-16 array is +** appended with a zero (null) character. +** +** Returns Length of UTF-16 array including null character. +** +*******************************************************************************/ +UINT16 utfc_8_to_16(UINT16 *p_utf16, UINT16 utf16_len, UINT8 *p_utf8) +{ + UINT32 ch; + UINT8 *p_end; + UINT16 *p; + UINT8 seq_len; + + /* sanity check destination buffer len */ + if (utf16_len == 0) + { + *p_utf16 = 0; + return 0; + } + + /* save space for null */ + utf16_len--; + + p = p_utf16; + p_end = (UINT8 *) p_utf8 + strlen((char *) p_utf8); + + while (*p_utf8) + { + /* get sequence length; skip if illegal */ + if ((seq_len = utfc_8_seq_len[*p_utf8 >> 4]) == 0) + { + p_utf8++; + continue; + } + + /* make sure sequence doesn't extend past end of UTF-8 buffer */ + if (p_utf8 + seq_len > p_end) + { + break; + } + + /* construct UTF-32 character from sequence */ + ch = 0; + switch (seq_len) + { /* note: everything falls through. */ + case 4: ch += *p_utf8++; ch <<= 6; + case 3: ch += *p_utf8++; ch <<= 6; + case 2: ch += *p_utf8++; ch <<= 6; + case 1: ch += *p_utf8++; + } + ch -= utfc_8_offset[seq_len - 1]; + + if (ch <= 0x0000FFFF) + { + /* UTF-16 surrogate values are illegal in UTF-32 */ + if (ch >= UTFC_SUR_HIGH_START && ch <= UTFC_SUR_LOW_END) + { + continue; + } + + /* make sure fits */ + if (p - p_utf16 == utf16_len) + { + break; + } + + *p++ = (UINT16) ch; + } + else if (ch < 0x0010FFFF) + { + /* make sure fits */ + if ((p - p_utf16) == (utf16_len - 1)) + { + break; + } + + ch -= 0x00010000; + *p++ = (UINT16) ((ch >> 10) + UTFC_SUR_HIGH_START); + *p++ = (UINT16) ((ch & 0x000003FF) + UTFC_SUR_LOW_START); + } + } + + /* set null */ + *p++ = 0; + + return (UINT16) (p - p_utf16); +} + diff --git a/stack/obx/obx_cact.c b/stack/obx/obx_cact.c new file mode 100644 index 0000000..62998bd --- /dev/null +++ b/stack/obx/obx_cact.c @@ -0,0 +1,1103 @@ +/***************************************************************************** +** +** Name: obx_cact.c +** +** File: OBEX Client State Machine Action Functions +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "obx_int.h" +#include "btm_api.h" + +const tOBX_EVENT obx_cl_state_2_event_map[] = +{ + OBX_DISCONNECT_RSP_EVT, /* OBX_CS_NOT_CONNECTED */ + OBX_SESSION_RSP_EVT, /* OBX_CS_SESSION_REQ_SENT */ + OBX_CONNECT_RSP_EVT, /* OBX_CS_CONNECT_REQ_SENT */ + OBX_NULL_EVT, /* OBX_CS_UNAUTH */ + OBX_ABORT_RSP_EVT, /* OBX_CS_CONNECTED */ + OBX_DISCONNECT_RSP_EVT, /* OBX_CS_DISCNT_REQ_SENT */ + OBX_NULL_EVT, /* OBX_CS_OP_UNAUTH */ + OBX_SETPATH_RSP_EVT, /* OBX_CS_SETPATH_REQ_SENT */ + OBX_ACTION_RSP_EVT, /* OBX_CS_ACTION_REQ_SENT */ + OBX_ABORT_RSP_EVT, /* OBX_CS_ABORT_REQ_SENT */ + OBX_PUT_RSP_EVT, /* OBX_CS_PUT_REQ_SENT */ + OBX_GET_RSP_EVT, /* OBX_CS_GET_REQ_SENT */ + OBX_PUT_RSP_EVT, /* OBX_CS_PUT_TRANSACTION */ + OBX_GET_RSP_EVT, /* OBX_CS_GET_TRANSACTION */ + OBX_PUT_RSP_EVT, /* OBX_CS_PUT_SRM */ + OBX_GET_RSP_EVT /* OBX_CS_GET_SRM */ +}; + +/******************************************************************************* +** Function obx_ca_close_sess_req +** Description send close session request +*******************************************************************************/ +BT_HDR * obx_ca_close_sess_req(tOBX_CL_CB *p_cb) +{ + BT_HDR *p_req; + UINT8 *p; + UINT8 num_trip = 0; + tOBX_TRIPLET triplet[4]; + UINT8 data[2]; + + p_req = OBX_HdrInit(OBX_HANDLE_NULL, OBX_MIN_MTU); + p = (UINT8 *) (p_req + 1) + p_req->offset; + /* Session request packet always has the final bit set */ + *p++ = (OBX_REQ_SESSION | OBX_FINAL); + p_req->len = 3; + p = data; + + /* add session opcode */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_OP; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_SESS_OP; + triplet[num_trip].p_array = p; + *p = OBX_SESS_OP_CLOSE; + p += OBX_LEN_SESS_PARAM_SESS_OP; + num_trip++; + + /* add session id */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_ID; + triplet[num_trip].len = OBX_SESSION_ID_SIZE; + triplet[num_trip].p_array = p_cb->sess_info; + num_trip++; + + OBX_AddTriplet(p_req, OBX_HI_SESSION_PARAM, triplet, num_trip); + /* adjust the packet len */ + p = (UINT8 *) (p_req + 1) + p_req->offset + 1; + UINT16_TO_BE_STREAM(p, p_req->len); + p_req->event = OBX_SESSION_REQ_EVT; + p_cb->sess_st = OBX_SESS_CLOSE; + OBX_TRACE_DEBUG2("obx_ca_close_sess_req shandle:0x%x, sess_st:%d", p_cb->ll_cb.comm.handle, p_cb->sess_st); + return p_req; +} + +/******************************************************************************* +** Function obx_ca_connect_req +** Description send connect request +*******************************************************************************/ +void obx_ca_connect_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + UINT8 msg[OBX_HDR_OFFSET + OBX_MAX_CONN_HDR_EXTRA]; + UINT8 *p = msg; + + /* Connect request packet always has the final bit set */ + *p++ = (OBX_REQ_CONNECT | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + *p++ = OBX_VERSION; + *p++ = OBX_CONN_FLAGS; + UINT16_TO_BE_STREAM(p, p_cb->ll_cb.port.rx_mtu); + + /* add session sequence number, if session is active */ + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = p_cb->ssn; + } + if (p_cb->srm) + { + p_cb->srm = OBX_SRM_ENABLE; + } + + /* IrOBEX spec forbids connection ID in Connect Request */ + p_pkt = obx_cl_prepend_msg(p_cb, p_pkt, msg, (UINT16)(p - msg) ); + + p_pkt->event = OBX_CONNECT_REQ_EVT; + obx_csm_event(p_cb, OBX_CONNECT_REQ_CEVT, p_pkt); +} + +/******************************************************************************* +** Function obx_ca_state +** Description change state +*******************************************************************************/ +tOBX_CL_STATE obx_ca_state(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + /* p_pkt should be NULL here */ + return p_cb->next_state; +} + +/******************************************************************************* +** Function obx_ca_start_timer +** Description start timer +*******************************************************************************/ +tOBX_CL_STATE obx_ca_start_timer(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + if (p_pkt) + GKI_freebuf(p_pkt); + obx_start_timer(&p_cb->ll_cb.comm); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_connect_ok +** Description process the connect OK response from server +*******************************************************************************/ +tOBX_CL_STATE obx_ca_connect_ok(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + UINT8 *p; + tOBX_EVT_PARAM param; /* The event parameter. */ + + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + /* reliable session was established - need to report the session event first */ + p = &p_cb->sess_info[OBX_SESSION_INFO_ID_IDX]; + UINT32_TO_BE_STREAM(p, p_cb->conn_id); + param.sess.p_sess_info= p_cb->sess_info; + param.sess.sess_st = p_cb->sess_st; + param.sess.nssn = p_cb->ssn; + param.sess.obj_offset = 0; + p = &p_cb->sess_info[OBX_SESSION_INFO_MTU_IDX]; + UINT16_TO_BE_STREAM(p, p_cb->param.conn.mtu); + param.sess.sess_op = OBX_SESS_OP_CREATE; + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_SESSION_RSP_EVT, p_cb->rsp_code, param, NULL); + } + return obx_ca_notify(p_cb, p_pkt); +} + +/******************************************************************************* +** Function obx_ca_session_ok +** Description process the session OK response from server +*******************************************************************************/ +tOBX_CL_STATE obx_ca_session_ok(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_TRIPLET triplet[OBX_MAX_SESS_PARAM_TRIP]; + UINT8 num = OBX_MAX_SESS_PARAM_TRIP; + UINT8 *p_nonce = NULL, *p_addr = NULL, *p_sess_id = NULL; + UINT8 *p, *p_cl_nonce; + UINT8 ind, nonce_len = 0; + UINT8 ind_sess_id; + BD_ADDR cl_addr; + tOBX_STATUS status = OBX_SUCCESS; + UINT8 nssn; +#if (BT_USE_TRACES == TRUE) + tOBX_SESS_ST old_sess_st = p_cb->sess_st; +#endif + tOBX_SESS_OP sess_op = OBX_SESS_OP_SET_TIME; + tOBX_CL_STATE new_state = OBX_CS_NULL; + tOBX_CL_EVENT sm_evt = OBX_BAD_SM_EVT; + UINT32 obj_offset = p_cb->param.sess.obj_offset; + UINT32 timeout = p_cb->param.sess.timeout; + tOBX_EVT_PARAM param; /* The event parameter. */ + UINT8 dropped = 0; + + OBX_TRACE_DEBUG4("obx_ca_session_ok sess_st: %d ssn:%d obj_offset:%d prev_state:%d", p_cb->sess_st, p_cb->ssn, obj_offset, p_cb->prev_state); + OBX_ReadTriplet(p_pkt, OBX_HI_SESSION_PARAM, triplet, &num); + obx_read_timeout (triplet, num, &timeout, &p_cb->sess_info[OBX_SESSION_INFO_TO_IDX]); + p_cb->param.sess.timeout = timeout; + if (p_cb->sess_st == OBX_SESS_SUSPEND) + { + p_cb->sess_st = OBX_SESS_SUSPENDED; + sess_op = OBX_SESS_OP_SUSPEND; + p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_cb->srm; + nssn = p_cb->ssn; + /* send a tx_empty event to close port */ + sm_evt = OBX_TX_EMPTY_CEVT; + OBX_TRACE_DEBUG2("suspend saved st:%d, srm:0x%x", p_cb->sess_info[OBX_SESSION_INFO_ST_IDX], p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + } + else if (p_cb->sess_st == OBX_SESS_CLOSE) + { + sess_op = OBX_SESS_OP_CLOSE; + p_cb->sess_st = OBX_SESS_NONE; + /* send a tx_empty event to close port */ + sm_evt = OBX_TX_EMPTY_CEVT; + } + else if (num) + { + ind_sess_id = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_ID); + if ((ind_sess_id != num) && (triplet[ind_sess_id].len == OBX_SESSION_ID_SIZE)) + { + p_sess_id = triplet[ind_sess_id].p_array; + } + switch (p_cb->sess_st) + { + case OBX_SESS_CREATE: + sess_op = OBX_SESS_OP_CREATE; + status = OBX_BAD_PARAMS; + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_ADDR); + if ((ind != num) && (triplet[ind].len == BD_ADDR_LEN)) + { + p_addr = triplet[ind].p_array; + } + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_NONCE); + if ((ind != num) && (triplet[ind].len >= OBX_MIN_NONCE_SIZE) && (triplet[ind].len <= OBX_NONCE_SIZE)) + { + p_nonce = triplet[ind].p_array; + nonce_len = triplet[ind].len; + } + + if (p_nonce && p_addr && p_sess_id) + { + OBX_TRACE_DEBUG0("verify session id"); + BTM_GetLocalDeviceAddr (cl_addr); + p_cl_nonce = &p_cb->sess_info[OBX_SESSION_ID_SIZE]; + p = p_cl_nonce; + UINT32_TO_BE_STREAM(p, p_cb->nonce); + /* calculate client copy of session id */ + obx_session_id (p_cb->sess_info, cl_addr, p_cl_nonce, OBX_LOCAL_NONCE_SIZE, p_addr, p_nonce, nonce_len); + obxu_dump_hex (p_cb->sess_info, "cl sess id", OBX_SESSION_ID_SIZE); + obxu_dump_hex (p_sess_id, "sr sess id", OBX_SESSION_ID_SIZE); + /* verify that the server copy is the same */ + if (memcmp (p_sess_id, p_cb->sess_info, OBX_SESSION_ID_SIZE) == 0) + { + p_cb->sess_st = OBX_SESS_ACTIVE; + p_cb->ssn = 0; + /* do we want a timer here */ + status = OBX_SUCCESS; + OBX_TRACE_DEBUG0("freeing received packet"); + GKI_freebuf (p_pkt) ; + p_pkt = p_cb->p_next_req; + p_cb->p_next_req = NULL; + obx_ca_connect_req (p_cb, p_pkt); + /* + p_cb->param.sess.p_sess_info= p_cb->sess_info; + p_cb->param.sess.sess_st = p_cb->sess_st; + p_cb->param.sess.nssn = p_cb->ssn; + p_cb->param.sess.sess_op = sess_op; + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_SESSION_RSP_EVT, p_cb->rsp_code, p_cb->param, NULL); + */ + return new_state; + } + } + break; + + case OBX_SESS_RESUME: + status = OBX_BAD_PARAMS; + dropped = p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] & OBX_CL_STATE_DROP; + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_NSEQNUM); + if ((ind == num) || (triplet[ind].len != 1)) + { + OBX_TRACE_ERROR0("RESUME:do not have valid NSSN tag"); + break; + } + + nssn = *(triplet[ind].p_array); + /* if SRM is enaged; make sure object offset TAG exists */ + if ((p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX] & OBX_SRM_ENGAGE) != 0) + { + obj_offset = obx_read_obj_offset(triplet, num); + OBX_TRACE_DEBUG2("RESUME:SRM is engaged and object offset:0x%x (0x%x)", + obj_offset, p_cb->param.sess.obj_offset); + + /* client always takes the offset and ssn from the response since adjustment was done at server side */ + p_cb->param.sess.obj_offset = obj_offset; + p_cb->ssn = nssn; + status = OBX_SUCCESS; + } + /* otherwise make sure NSSN from server is OK */ + else if (nssn == p_cb->ssn) + { + OBX_TRACE_DEBUG0("RESUME:nssn matches expected ssn"); + status = OBX_SUCCESS; + } + else if (dropped != 0) + { + OBX_TRACE_DEBUG2("RESUME:link drop suspend nssn:%d cb ssn:%d", nssn, p_cb->ssn); + if ((UINT8)(nssn+1) == p_cb->ssn) + { + OBX_TRACE_DEBUG0("RESUME:nssn matches expected(ssn-1)"); + p_cb->ssn -= 1; + status = OBX_SUCCESS; + } + else if (nssn == (UINT8)(p_cb->ssn+1)) + { + OBX_TRACE_DEBUG0("RESUME:nssn matches expected(ssn+1)"); + nssn -= 1; + status = OBX_SUCCESS; + } + } + else + { + OBX_TRACE_ERROR2("RESUME:bad NSSN:%d (%d)", nssn, p_cb->ssn); + break; + } + p_cb->sess_st = OBX_SESS_ACTIVE; + sess_op = OBX_SESS_OP_RESUME; + OBX_TRACE_DEBUG2("RESUME:info new_state:0x%x, srm:0x%x", p_cb->sess_info[OBX_SESSION_INFO_ST_IDX], p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + p_cb->srm = p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]; + p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] &= ~OBX_CL_STATE_DROP; + if (p_cb->srm & OBX_SRM_ENGAGE) + { + new_state = p_cb->sess_info[OBX_SESSION_INFO_ST_IDX]; + if (new_state == OBX_CS_GET_SRM) + { + p_cb->srm |= OBX_SRM_WAIT_UL; + /* Adjust snn in the control block since it is off by one with nssn in resume request */ + p_cb->ssn--; + } + } + else + { + new_state = OBX_CS_CONNECTED; + p_cb->srmp |= OBX_SRMP_SESS_FST; + } + OBX_TRACE_DEBUG2("RESUME:new_state:%d, srm:0x%x", new_state, p_cb->srm); + break; + + default: + status = OBX_BAD_PARAMS; + } + } + else + { + status = OBX_BAD_PARAMS; + } + OBX_TRACE_DEBUG5("obx_ca_session_ok prev:%d, sess_st:%d->%d obj_offset:%d status:%d", p_cb->prev_state, old_sess_st, p_cb->sess_st, obj_offset, status); + + if (sess_op == OBX_SESS_OP_SET_TIME) + new_state = p_cb->prev_state; + + if (status != OBX_SUCCESS) + { + if (p_cb->sess_st == OBX_SESS_CLOSE) + p_cb->sess_st = OBX_SESS_NONE; + obx_csm_event(p_cb, OBX_TIMEOUT_CEVT, NULL); + return OBX_CS_NULL; + } + p_cb->param.sess.p_sess_info= p_cb->sess_info; + p_cb->param.sess.sess_st = p_cb->sess_st; + p_cb->param.sess.nssn = nssn; + p_cb->param.sess.ssn = nssn; + p_cb->param.sess.sess_op = sess_op; + p_cb->param.sess.obj_offset = obj_offset; + p_cb->param.sess.timeout = timeout; + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_SESSION_RSP_EVT, p_cb->rsp_code, p_cb->param, NULL); + + if ((sess_op == OBX_SESS_OP_RESUME) && (p_cb->sess_st == OBX_SESS_ACTIVE)) + { + param.conn.ssn = p_cb->ssn; + memcpy (param.conn.peer_addr, p_cb->peer_addr, BD_ADDR_LEN); + p = &p_cb->sess_info[OBX_SESSION_INFO_MTU_IDX]; + BE_STREAM_TO_UINT16(param.conn.mtu, p); + p_cb->ll_cb.comm.tx_mtu = param.conn.mtu; + param.conn.handle = p_cb->ll_cb.comm.handle; + OBX_TRACE_DEBUG1("RESUME: tx_mtu: %d", p_cb->ll_cb.comm.tx_mtu); + /* report OBX_CONNECT_RSP_EVT to let the client know the MTU */ + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_CONNECT_RSP_EVT, OBX_RSP_OK, param, NULL); + sm_evt = OBX_STATE_CEVT; + p_cb->next_state = OBX_CS_CONNECTED; + } + + if (sm_evt != OBX_BAD_SM_EVT) + { + /* send an event to csm */ + obx_csm_event(p_cb, sm_evt, NULL); + } + return new_state; +} + +/******************************************************************************* +** Function obx_ca_session_cont +** Description process the continue response from server for SRM after +** a suspend session request is sent +*******************************************************************************/ +tOBX_CL_STATE obx_ca_session_cont(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + BOOLEAN free = TRUE; + + OBX_TRACE_DEBUG3("obx_ca_session_cont sess_st:%d prev_state:%d, srm:0x%x", p_cb->sess_st, p_cb->prev_state, p_cb->srm); + if (p_cb->sess_st == OBX_SESS_SUSPEND) + { + if (p_cb->prev_state == OBX_CS_GET_SRM) + { + if ((p_cb->srm & OBX_SRM_WAIT_UL) == 0) + { + p_cb->srm |= OBX_SRM_WAIT_UL; + p_cb->api_evt = OBX_GET_RSP_EVT; + } + else + { + GKI_enqueue_head (&p_cb->ll_cb.comm.rx_q, p_pkt); + OBX_TRACE_DEBUG1("obx_ca_session_cont rx_q.count:%d", p_cb->ll_cb.comm.rx_q.count); + } + free = FALSE; + } + else if (p_cb->prev_state == OBX_CS_GET_REQ_SENT) + { + p_cb->api_evt = OBX_GET_RSP_EVT; + free = FALSE; + } + + } + if (free && p_pkt) + GKI_freebuf(p_pkt); + OBX_TRACE_DEBUG1("obx_ca_session_cont srm: 0x%x(e)", p_cb->srm ); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_session_get +** Description process the get req api for SRM after +** a suspend session request is sent (to clean out the received buffers) +*******************************************************************************/ +tOBX_CL_STATE obx_ca_session_get(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + OBX_TRACE_DEBUG3("obx_ca_session_get sess_st:%d prev_state: %d, srm:0x%x", p_cb->sess_st, p_cb->prev_state, p_cb->srm ); + if (p_cb->sess_st == OBX_SESS_SUSPEND && p_cb->prev_state == OBX_CS_GET_SRM) + return obx_ca_srm_get_req(p_cb, p_pkt); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_session_fail +** Description process the session failed response from server +*******************************************************************************/ +tOBX_CL_STATE obx_ca_session_fail(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SESS_ST old_sess_st = p_cb->sess_st; + + p_cb->sess_st = OBX_SESS_NONE; + OBX_TRACE_DEBUG2("obx_ca_session_fail, sess_st:%d->%d", old_sess_st, p_cb->sess_st); + if (old_sess_st == OBX_SESS_CREATE && p_cb->rsp_code != OBX_RSP_OK) + { + /* peer device does not support session. Continue with regular session */ + /* report session failure */ + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_SESSION_RSP_EVT, p_cb->rsp_code, p_cb->param, NULL); + OBX_TRACE_DEBUG0("freeing received packet"); + if (p_pkt) + GKI_freebuf (p_pkt) ; + p_pkt = p_cb->p_next_req; + p_cb->p_next_req = NULL; + obx_ca_connect_req (p_cb, p_pkt); + } + else + { + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_SESSION_RSP_EVT, OBX_RSP_FAILED, p_cb->param, NULL); + obx_ca_close_port (p_cb, p_pkt); + } + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_abort +** Description process the abort request in connected state +*******************************************************************************/ +tOBX_CL_STATE obx_ca_abort(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + OBX_TRACE_DEBUG2("obx_ca_abort srm:0x%x srmp:0x%x", p_cb->srm, p_cb->srmp); + if ( p_cb->srmp & OBX_SRMP_SESS_FST) + { + /* the first request after a session is resume. + * We may need to abort the previous request */ + if (p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] != OBX_CS_CONNECTED) + { + /* set the state here, just in case the result of obx_ca_snd_req is partial_sent */ + p_cb->state = OBX_CS_ABORT_REQ_SENT; + obx_ca_snd_req(p_cb, p_pkt); + return OBX_CS_NULL; + } + p_cb->srmp &= ~OBX_SRMP_SESS_FST; + } + obx_ca_notify (p_cb, p_pkt); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_snd_put_req +** Description send put request in connected state +*******************************************************************************/ +tOBX_CL_STATE obx_ca_snd_put_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_NULL; + + OBX_TRACE_DEBUG1("obx_ca_snd_put_req, srm:0x%x", p_cb->srm); + state = obx_ca_snd_req (p_cb, p_pkt); + if (p_cb->srm & OBX_SRM_ENGAGE) + { + if (state == OBX_CS_PARTIAL_SENT) + { + p_cb->next_state = OBX_CS_PUT_SRM; + } + else + { + p_cb->state = OBX_CS_PUT_SRM; + if ((p_cb->srm & OBX_SRM_WAIT) == 0) + { + p_cb->rsp_code = OBX_RSP_CONTINUE; + p_cb->param.put.final = FALSE; + p_cb->param.put.type = OBX_PT_PUT; + p_cb->param.put.ssn = 0; + state = obx_ca_notify (p_cb, NULL); + } + } + } + return state; +} + +/******************************************************************************* +** Function obx_ca_snd_get_req +** Description send get request in connected state +*******************************************************************************/ +tOBX_CL_STATE obx_ca_snd_get_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state; + + OBX_TRACE_DEBUG1("obx_ca_snd_get_req srm:0x%x", p_cb->srm ); + state = obx_ca_snd_req (p_cb, p_pkt); + OBX_TRACE_DEBUG1("srm:0x%x", p_cb->srm ); + if (p_cb->srm & OBX_SRM_ENABLE) + { + if (state == OBX_CS_PARTIAL_SENT) + { + p_cb->next_state = OBX_CS_GET_SRM; + } + else + state = OBX_CS_GET_SRM; + p_cb->srm &= ~OBX_SRM_WAIT_UL; + } + OBX_TRACE_DEBUG1("srm:0x%x", p_cb->srm ); + return state; +} + +/******************************************************************************* +** Function obx_ca_srm_snd_req +** Description send Abort or Disconnect request +*******************************************************************************/ +tOBX_CL_STATE obx_ca_srm_snd_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state; + tOBX_COMM_CB *p_comm = &p_cb->ll_cb.comm; + + OBX_TRACE_DEBUG2("obx_ca_srm_snd_req rx_q.count: %d, srm:0x%x", p_comm->rx_q.count, p_cb->srm ); + p_cb->srm &= ~OBX_SRM_WAIT_UL; + state = obx_ca_snd_req (p_cb, p_pkt); + if ((p_pkt = (BT_HDR *)GKI_dequeue (&p_comm->rx_q)) != NULL) + { + GKI_freebuf(p_pkt); + } + OBX_TRACE_DEBUG2(" rx_q.count: %d, srm:0x%x", p_comm->rx_q.count, p_cb->srm ); + return state; +} + +/******************************************************************************* +** Function obx_ca_srm_put_req +** Description send a PUT request when SRM is engaged +*******************************************************************************/ +tOBX_CL_STATE obx_ca_srm_put_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state; + + OBX_TRACE_DEBUG1("obx_ca_srm_put_req srm:0x%x", p_cb->srm ); + state = obx_ca_snd_req (p_cb, p_pkt); + OBX_TRACE_DEBUG4("obx_ca_srm_put_req state:%d srm:0x%x, final:%d rsp_code:0x%x", state, p_cb->srm, p_cb->final, p_cb->rsp_code ); + if (state != OBX_CS_PARTIAL_SENT && p_cb->final != TRUE && (p_cb->srm & OBX_SRM_WAIT) == 0) + { + p_cb->rsp_code = OBX_RSP_CONTINUE; + state = obx_ca_notify (p_cb, NULL); + } + return state; +} + +/******************************************************************************* +** Function obx_ca_srm_get_req +** Description send a GET request when SRM is engaged +*******************************************************************************/ +tOBX_CL_STATE obx_ca_srm_get_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_NULL; + tOBX_COMM_CB *p_comm = &p_cb->ll_cb.comm; + + OBX_TRACE_DEBUG3("obx_ca_srm_get_req rx_q.count: %d, srm:0x%x", p_cb->sess_st, p_comm->rx_q.count, p_cb->srm ); + + obx_start_timer(&p_cb->ll_cb.comm); + p_cb->srm &= ~OBX_SRM_WAIT_UL; + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + p_cb->ssn++; + } + if (p_pkt) + GKI_freebuf(p_pkt); + + if ((p_pkt = (BT_HDR *)GKI_dequeue (&p_comm->rx_q)) != NULL) + { + if (state != OBX_CS_NULL) + { + p_cb->prev_state = p_cb->state; + p_cb->state = state; + state = OBX_CS_NULL; + } + obx_cl_proc_pkt (p_cb, p_pkt); + obx_flow_control(p_comm); + OBX_TRACE_DEBUG1("obx_ca_srm_get_req rx_q.count: %d", p_cb->ll_cb.comm.rx_q.count ); + } + OBX_TRACE_DEBUG1("obx_ca_srm_get_req srm:0x%x", p_cb->srm ); + + return state; +} + +/******************************************************************************* +** Function obx_ca_srm_put_notify +** Description Notify the OBX user OBX_PUT_RSP_EVT (OBX is ready for next req) +*******************************************************************************/ +tOBX_CL_STATE obx_ca_srm_put_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state; + + OBX_TRACE_DEBUG2("obx_ca_srm_put_notify srm: 0x%x, srmp: 0x%x", p_cb->srm, p_cb->srmp ); + + state = obx_ca_notify (p_cb, p_pkt); + return state; +} + +/******************************************************************************* +** Function obx_ca_srm_get_notify +** Description Notify the OBX user OBX_GET_RSP_EVT (OBX is ready for next req) +*******************************************************************************/ +tOBX_CL_STATE obx_ca_srm_get_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_NULL; + + OBX_TRACE_DEBUG1("obx_ca_srm_get_notify srm: 0x%x", p_cb->srm ); + /* do not allow SRMP for now */ + p_cb->srm &= ~OBX_SRM_WAIT; + + if (p_cb->srm & OBX_SRM_ENGAGE) + { + if ((p_cb->srm & OBX_SRM_WAIT_UL) == 0) + { + p_cb->srm |= OBX_SRM_WAIT_UL; + state = obx_ca_notify (p_cb, p_pkt); + } + else + { + GKI_enqueue_head (&p_cb->ll_cb.comm.rx_q, p_pkt); + OBX_TRACE_DEBUG1("obx_ca_srm_get_notify rx_q.count:%d", p_cb->ll_cb.comm.rx_q.count); + } + } + else + { + state = obx_ca_notify (p_cb, p_pkt); + if (state == OBX_CS_GET_SRM || state == OBX_CS_NULL) + state = OBX_CS_GET_TRANSACTION; + } + OBX_TRACE_DEBUG2("obx_ca_srm_get_notify srm: 0x%x(e) state:%s", p_cb->srm, obx_sr_get_state_name(state) ); + return state; +} + +/******************************************************************************* +** Function obx_ca_save_rsp +** Description save response in control block +*******************************************************************************/ +tOBX_CL_STATE obx_ca_save_rsp(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + GKI_enqueue (&p_cb->ll_cb.comm.rx_q, p_pkt); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_save_req +** Description save request in control block +*******************************************************************************/ +tOBX_CL_STATE obx_ca_save_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + if (p_cb->p_next_req) + { + /* this probably would not happen */ + /* this action only occurs when we are flow controlled by the peer + * and the client wants to abort the operation */ + /* Just in case that the user keeps calling abort request.... */ + OBX_TRACE_WARNING1("free next req: 0x%x", p_cb->p_next_req ); + GKI_freebuf(p_cb->p_next_req); + } + + p_cb->p_next_req = p_pkt; + + return OBX_CS_NULL; +} + + +/******************************************************************************* +** Function obx_ca_snd_req +** Description If (p_pkt), call p_send_fn() to send the message to the peer. +** Start timer. Return NULL state.If data is partially sent, return +** PART state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_snd_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_NULL; + UINT8 rsp_code = OBX_RSP_DEFAULT; + + obx_access_rsp_code(p_pkt, &rsp_code); + p_cb->final = (rsp_code&OBX_FINAL) ? TRUE : FALSE; + OBX_TRACE_DEBUG2("obx_ca_snd_req rsp_code: 0x%x final:%d", rsp_code, p_cb->final ); + + /* save a copy of the request sent to the server */ + /* In case that we are challenged by the server, + * we can send the same request with authentication response again */ + if (p_cb->p_saved_req) + GKI_freebuf(p_cb->p_saved_req); + + p_cb->p_saved_req = obx_dup_pkt(p_pkt); + + OBX_TRACE_DEBUG3( "event p_saved_req:%d, pkt:%d, final: %d", p_cb->p_saved_req->event, p_pkt->event,p_cb->final); + + p_cb->ll_cb.comm.p_txmsg = p_pkt; + /* debug: obxu_dump_hex ((UINT8 *)(p_pkt + 1) + p_pkt->offset, "conn req", p_pkt->len); */ + + p_cb->srmp &= ~OBX_SRMP_SESS_FST; + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + p_cb->ssn++; + } + + if (p_cb->ll_cb.comm.p_send_fn(&p_cb->ll_cb) == FALSE) + { + p_cb->next_state = p_cb->state; + state = OBX_CS_PARTIAL_SENT; + } + + return state; +} + +/******************************************************************************* +** Function obx_ca_close_port +** Description Close the transport. Return NULL state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_close_port(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + if (p_pkt) + GKI_freebuf(p_pkt); + p_cb->ll_cb.comm.p_close_fn(p_cb->ll_cb.comm.id); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_snd_part +** Description Call p_send_fn() to send the left-over OBEX message to the peer. +** Start timer. If all the data is sent, call obx_csm_event() with +** STATE event to next_state in the port control block. +** If (p_next_req), call obx_csm_event() to process the saved request. +** Return NULL state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_snd_part(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_CL_STATE state = OBX_CS_NULL; + + OBX_TRACE_DEBUG1("obx_ca_snd_part sess_st:%d", p_cb->sess_st); + + /* p_pkt should be NULL here */ + if (p_cb->ll_cb.comm.p_send_fn(&p_cb->ll_cb) == TRUE) + { + /* data is all sent. change state to the appropriate state */ + obx_csm_event(p_cb, OBX_STATE_CEVT, NULL); + if (p_cb->p_next_req && (p_cb->sess_st != OBX_SESS_CREATE)) + { + /* abort request was issued - send it now */ + p_pkt = p_cb->p_next_req; + p_cb->p_next_req = NULL; + obx_csm_event(p_cb, (tOBX_CL_EVENT)(p_pkt->event-1), p_pkt); + } + + OBX_TRACE_DEBUG2("obx_ca_snd_part state:%d, srm:0x%x", p_cb->state, p_cb->srm); + if ((p_pkt = (BT_HDR *)GKI_dequeue (&p_cb->ll_cb.comm.rx_q)) != NULL) + { + obx_cl_proc_pkt (p_cb, p_pkt); + } + else if (p_cb->state == OBX_CS_PUT_SRM) + { + if (((p_cb->srm & OBX_SRM_WAIT) == 0) && (p_cb->final != TRUE)) + { + state = obx_ca_notify (p_cb, NULL); + } + } + } + return state; +} + +/******************************************************************************* +** Function obx_ca_connect_error +** Description Call callback function with OBX_CLOSE_IND_EVT. Free the client +** control block. Return NULL state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_connect_error(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_CL_CBACK *p_cback = p_cb->p_cback; + tOBX_HANDLE handle = p_cb->ll_cb.comm.handle; + tOBX_EVT_PARAM param = p_cb->param; + tOBX_SR_STATE save_state; + + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + /* The transport is interrupted while a reliable session is active: + * report a suspend event fot application to save the information in NV. + * The only time this is called is for port close evt /w next state as not_connected + * we need to use prev_state as the potential state to resume session + */ + save_state = p_cb->prev_state; + if (save_state == OBX_CS_PARTIAL_SENT) + save_state = p_cb->next_state; + /* marks link drop suspend only when SRM is not engaged */ + if ((p_cb->srm & OBX_SRM_ENGAGE) == 0) + save_state |= OBX_CL_STATE_DROP; + else if (save_state == OBX_CS_GET_SRM) + p_cb->srm &= ~OBX_SRM_WAIT_UL; + p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] = save_state; + OBX_TRACE_DEBUG2("obx_ca_connect_error saved state:0x%x, srm:0x%x", save_state, p_cb->srm); + p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_cb->srm; + param.sess.p_sess_info = p_cb->sess_info; + param.sess.sess_op = OBX_SESS_OP_TRANSPORT; + param.sess.sess_st = p_cb->sess_st; + param.sess.nssn = p_cb->ssn; + param.sess.ssn = p_cb->ssn; + param.sess.obj_offset = 0; + param.sess.timeout = OBX_SESS_TIMEOUT_VALUE; + memcpy(param.sess.peer_addr, p_cb->peer_addr, BD_ADDR_LEN); + p_cb->sess_st = OBX_SESS_NONE; + (*p_cback)(handle, OBX_SESSION_INFO_EVT, OBX_RSP_OK, param, NULL); + } + + obx_cl_free_cb(p_cb); + (*p_cback)(handle, OBX_CLOSE_IND_EVT, OBX_RSP_DEFAULT, param, p_pkt); + return OBX_CS_NULL; +} + +/******************************************************************************* +** Function obx_ca_connect_fail +** Description If the response code is OBX_RSP_UNAUTHORIZED, save the OBEX +** message in the client control block and call callback function +** with OBX_PASSWORD_EVT. Return NULL state.Otherwise, call +** obx_csm_event() with OBX_DISCNT_REQ_CEVT. (We do not need to +** send disconnect request to the server, since we are not +** connected yet). Return NULL state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_connect_fail(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_NULL; + + if ( p_cb->rsp_code == OBX_RSP_UNAUTHORIZED && + OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL) + { + p_cb->api_evt = OBX_PASSWORD_EVT; + p_cb->p_auth = obx_dup_pkt(p_pkt); + } + else + { + /* Connect Request is rejected for reasons other than authentication + * or if the client challenges the server, but the server does nt return good digest + * - notify the user of the failure and .. */ + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, OBX_CONNECT_RSP_EVT, p_cb->rsp_code, p_cb->param, p_pkt); + p_cb->api_evt = OBX_NULL_EVT; + /* and close the RFCOMM port */ + obx_csm_event(p_cb, OBX_DISCNT_REQ_CEVT, NULL); + } + return state; +} + +/******************************************************************************* +** Function obx_ca_discnt_req +** Description OBX_DISCNT_REQ_CEVT event is received in OBX_CS_CONNECT_REQ_SENT +** state. In case that the server fails the authentication, client +** needs to send Disconnect Req. Otherwise, just close port. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_discnt_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_CL_STATE state = OBX_CS_NOT_CONNECTED; + UINT8 msg[OBX_HDR_OFFSET]; + UINT8 *p = msg; + + if (p_cb->wait_auth == OBX_WAIT_AUTH_FAIL) + { + /* server thinks the connection is up. + * client needs to send disconnect req */ + state = OBX_CS_DISCNT_REQ_SENT; + /* Disconnect request always has the final bit set */ + *p++ = (OBX_REQ_DISCONNECT|OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + /* add session sequence number, if session is active */ + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = p_cb->ssn; + } + + /* add connection ID, if needed */ + if (p_cb->conn_id != OBX_INVALID_CONN_ID) + { + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_cb->conn_id); + } + + p_pkt = obx_cl_prepend_msg(p_cb, NULL, msg, (UINT16)(p - msg) ); + obx_ca_snd_req(p_cb, p_pkt); + } + else + { + /* connection is not officially up yet. + * just close the port */ + obx_ca_close_port(p_cb, p_pkt); + } + p_cb->wait_auth = FALSE; + + return state; +} + +/******************************************************************************* +** Function obx_ca_notify +** Description Use api_evt or look up the event according to the state. Fill +** the event parameter. Call callback function with the event. +** Return NULL state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_CONNECTED; + tOBX_EVENT event = obx_cl_state_2_event_map[p_cb->state - 1]; + BOOLEAN notify = FALSE; + tOBX_CL_EVENT sm_evt = OBX_BAD_SM_EVT; + BT_HDR *p_req = NULL; + + OBX_TRACE_DEBUG6( "obx_ca_notify state: %s, prev_state: %s, rsp:0x%x, sess_st:%d, event:%d srm:0x%x", + obx_cl_get_state_name( p_cb->state ), obx_cl_get_state_name(p_cb->prev_state), p_cb->rsp_code, p_cb->sess_st, event, p_cb->srm); + OBX_TRACE_DEBUG2( "ssn:0x%x/0x%x", p_cb->ssn, p_cb->param.ssn); + + if ( (p_cb->final == TRUE && p_cb->rsp_code == OBX_RSP_CONTINUE && p_cb->state == OBX_CS_PUT_TRANSACTION) || + (p_cb->final == FALSE && p_cb->rsp_code != OBX_RSP_CONTINUE) ) + { + /* final bit on the request mismatch the responde code --- Error!! */ + OBX_TRACE_ERROR2( "final:%d on the request mismatch the responde code:0x%x", + p_cb->final, p_cb->rsp_code) ; + /* change the state to not connected state */ + p_cb->next_state = OBX_CS_NOT_CONNECTED; + obx_csm_event(p_cb, OBX_STATE_CEVT, NULL); + notify = TRUE; + /* send a tx_empty event to close port */ + sm_evt = OBX_TX_EMPTY_CEVT; + } + + else if (event != OBX_NULL_EVT) + { + switch (p_cb->state) + { + case OBX_CS_PUT_TRANSACTION: + case OBX_CS_GET_TRANSACTION: + case OBX_CS_PUT_REQ_SENT: + case OBX_CS_GET_REQ_SENT: + case OBX_CS_PUT_SRM: + case OBX_CS_GET_SRM: + if (p_cb->rsp_code == OBX_RSP_CONTINUE ) + { + /* notify the event in this function. the new state stays the same */ + notify = TRUE; + + if (p_cb->srm & OBX_SRM_ENGAGE) + { + if (p_cb->state == OBX_CS_PUT_TRANSACTION) + { + p_cb->state = OBX_CS_PUT_SRM; + } + else if (p_cb->state == OBX_CS_GET_TRANSACTION) + { + p_cb->state = OBX_CS_GET_SRM; + } + else if (p_cb->state == OBX_CS_PUT_SRM && p_pkt && (p_cb->srm & OBX_SRM_WAIT) == 0) + { + OBX_TRACE_ERROR0 ("unexpected PUT response. disconnect now!!"); + notify = FALSE; + event = OBX_NULL_EVT; + obx_ca_close_port(p_cb, p_pkt); + } + /* clear the wait bit here to avoid the link being disconnected by accident */ + p_cb->srm &= ~OBX_SRM_WAIT; + if (p_cb->srmp) + { + p_cb->srmp = 0; + p_cb->srm |= OBX_SRM_WAIT; + } + } + } + /* else let obx_csm_event notify the event. the new state is OBX_CS_CONNECTED */ + else + { + /* dis-engage SRM */ + p_cb->srm &= OBX_SRM_ENABLE; + OBX_TRACE_DEBUG1( "disengage srm:0x%x", p_cb->srm); + } + break; + + case OBX_CS_NOT_CONNECTED: + if (p_cb->sess_st == OBX_SESS_ACTIVE && p_cb->prev_state == OBX_CS_DISCNT_REQ_SENT) + { + p_req = obx_ca_close_sess_req (p_cb); + sm_evt = OBX_SESSION_REQ_CEVT; + state = OBX_CS_NULL; + } + else + { + notify = TRUE; + /* send a tx_empty event to close port */ + sm_evt = OBX_TX_EMPTY_CEVT; + } + break; + + case OBX_CS_CONNECT_REQ_SENT: + if (p_cb->rsp_code == OBX_RSP_FAILED) + { + /* client challenged the server and the server does not return a good digest */ + notify = TRUE; + /* send a disconnect req event to close port */ + sm_evt = OBX_DISCNT_REQ_CEVT; + } + break; + + case OBX_CS_ABORT_REQ_SENT: + p_cb->srm &= OBX_SRM_ENABLE; + OBX_TRACE_DEBUG1( "(ab) disengage srm:0x%x", p_cb->srm); + break; + } + } + + if (notify == TRUE ) + { + (*p_cb->p_cback)(p_cb->ll_cb.comm.handle, event, p_cb->rsp_code, p_cb->param, p_pkt); + event = OBX_NULL_EVT; + } + + if (sm_evt != OBX_BAD_SM_EVT) + { + /* send an event to csm */ + obx_csm_event(p_cb, sm_evt, p_req); + } + + p_cb->api_evt = event; + if (event == OBX_NULL_EVT) + { + state = OBX_CS_NULL; + } + else + { + p_cb->ssn = p_cb->param.ssn; + OBX_TRACE_DEBUG1( "ssn:0x%x", p_cb->ssn); + } + + return state; +} + +/******************************************************************************* +** Function obx_ca_fail_rsp +** Description Save the OBEX message in control block.If the response code is +** OBX_RSP_UNAUTHORIZED, set api_evt to OBX_PASSWORD_EVT. Return +** OP_UNAUTH state.Otherwise, set api_evt according to the +** event/state table. Return CONN state. +*******************************************************************************/ +tOBX_CL_STATE obx_ca_fail_rsp(tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_CS_CONNECTED; + + p_cb->srm &= OBX_SRM_ENABLE; + + if ( p_cb->rsp_code == OBX_RSP_UNAUTHORIZED && + OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL) + { + state = OBX_CS_OP_UNAUTH; + p_cb->api_evt = OBX_PASSWORD_EVT; + p_cb->p_auth = obx_dup_pkt(p_pkt); + } + else + p_cb->api_evt = obx_cl_state_2_event_map[p_cb->state - 1]; + + return state; +} diff --git a/stack/obx/obx_capi.c b/stack/obx/obx_capi.c new file mode 100644 index 0000000..ea34aa1 --- /dev/null +++ b/stack/obx/obx_capi.c @@ -0,0 +1,855 @@ +/***************************************************************************** +** +** Name: obx_capi.c +** +** File: OBEX Client Application Programming Interface functions +** +** Copyright (c) 2003-2010, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include "bt_target.h" +#include "wcassert.h" +#include "obx_int.h" +#include "port_api.h" +#include "l2c_api.h" +#include "btm_api.h" + +/******************************************************************************* +** +** Function OBX_ConnectReq +** +** Description This function registers a client entity to OBEX and sends a +** CONNECT request to the server specified by the API parameters. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_ConnectReq(BD_ADDR bd_addr, UINT8 scn, UINT16 mtu, + tOBX_CL_CBACK *p_cback, tOBX_HANDLE *p_handle, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + UINT8 msg[OBX_HDR_OFFSET + OBX_MAX_CONN_HDR_EXTRA]; + UINT8 *p = msg; + tOBX_CL_CB *p_cb; + + WC_ASSERT(p_handle); + + p_cb = obx_cl_get_cb(*p_handle); + if (p_cb == NULL) + p_cb = obx_cl_alloc_cb(); + + if (p_cb) + { + if (p_cb->ll_cb.port.port_handle == 0) + { + WC_ASSERT(p_cback); + /* port is not open yet- open one + * this is the first CONNECT request */ + p_cb->ll_cb.comm.rx_mtu = mtu; + p_cb->p_cback = p_cback; + p_cb->state = OBX_CS_NOT_CONNECTED; + status = obx_open_port(&p_cb->ll_cb.port, bd_addr, scn); + *p_handle = p_cb->ll_cb.port.handle; + } + else + { + /* when called by other OBX functions */ + status = OBX_SUCCESS; + } + + if (status == OBX_SUCCESS) + { + /* Connect request packet always has the final bit set */ + *p++ = (OBX_REQ_CONNECT | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + *p++ = OBX_VERSION; + *p++ = OBX_CONN_FLAGS; + UINT16_TO_BE_STREAM(p, p_cb->ll_cb.port.rx_mtu); + /* IrOBEX spec forbids connection ID in Connect Request */ + p_pkt = obx_cl_prepend_msg(p_cb, p_pkt, msg, (UINT16)(p - msg) ); + + p_pkt->event = OBX_CONNECT_REQ_EVT; + obx_csm_event(p_cb, OBX_CONNECT_REQ_CEVT, p_pkt); + } + else + { + OBX_TRACE_ERROR1("Error opening port for scn: %d", scn); + obx_cl_free_cb(p_cb); + } + } + return status; +} + +/******************************************************************************* +** +** Function OBX_AllocSession +** +** Description This function registers a client entity to OBEX. +** If p_session_info is not NULL, it tries to find an suspended session +** with matching session_info. +** If scn is not 0, it allocates a control block for this new session. +** Otherwise, it allocates a control block for the given PSM. +** The associated virtual PSM assigned by L2CAP is returned in p_psm +** The allocated OBEX handle is returned in p_handle. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_AllocSession (UINT8 *p_session_info, UINT8 scn, UINT16 *p_psm, + tOBX_CL_CBACK *p_cback, tOBX_HANDLE *p_handle) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + tOBX_CL_CB *p_cb; + + WC_ASSERT(p_handle); + WC_ASSERT(p_cback); + + OBX_TRACE_API2("OBX_AllocSession scn: %d, psm:0x%x", scn, *p_psm); + + if (p_session_info) + { + p_cb = obx_cl_get_suspended_cb(p_handle, p_session_info); + if (p_cb) + { + p_cb->srm = p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]; + status = OBX_SUCCESS; + } + } + else + { + p_cb = obx_cl_get_cb(*p_handle); + if (p_cb == NULL) + p_cb = obx_cl_alloc_cb(); + + + } + + if (p_cb) + { + p_cb->rsp_code = 0; + p_cb->psm = 0; + + if (p_cb->sess_st != OBX_SESS_SUSPENDED) + p_cb->sess_st = OBX_SESS_NONE; + + if (p_psm && L2C_IS_VALID_PSM(*p_psm)) + { + obx_register_l2c(p_cb, *p_psm); + /* obx_register_l2c puts the virtual psm in p_cb->psm */ + if (p_cb->psm) + { + *p_psm = p_cb->psm; + status = OBX_SUCCESS; + } + } + + /* check SCN only when a virtual PSM is not allocated */ + if (!p_cb->psm) + { + if (scn) + { + /* borrow this data member temporarily */ + p_cb->rsp_code = scn; + status = OBX_SUCCESS; + } + } + } + + if (status != OBX_SUCCESS) + { + obx_cl_free_cb(p_cb); + p_cb = NULL; + } + + if (p_cb) + { + *p_handle = p_cb->ll_cb.comm.handle; + p_cb->p_cback = p_cback; + p_cb->state = OBX_CS_NOT_CONNECTED; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_CreateSession +** +** Description This function registers a client entity to OBEX. +** It may send a CreateSession request and wait for CreateSession response. +** It sends a CONNECT request to the server specified by the API parameters. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_CreateSession (BD_ADDR bd_addr, UINT16 mtu, BOOLEAN srm, UINT32 nonce, + tOBX_HANDLE handle, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + tOBX_CL_CB *p_cb; + UINT8 *p; + UINT8 *pn; + BT_HDR *p_req; + UINT8 data[20]; + tOBX_TRIPLET triplet[4]; + UINT8 num_trip = 0; + + OBX_TRACE_API1("OBX_CreateSession handle: 0x%x", handle); + p_cb = obx_cl_get_cb(handle); + + if (p_cb) + { + if (p_cb->state != OBX_CS_NOT_CONNECTED || p_cb->sess_st != OBX_SESS_NONE) + { + OBX_TRACE_ERROR2("bad state: %d, or sess_st:%d", p_cb->state, p_cb->sess_st); + return status; + } + + if (p_cb->ll_cb.comm.id == 0) + { + p_cb->ll_cb.comm.rx_mtu = mtu; + + OBX_TRACE_DEBUG2("scn: %d, psm:0x%x", p_cb->rsp_code, p_cb->psm); + + if (p_cb->psm) + { + /* L2CAP channel is not open yet- open one + * this is the first CONNECT request */ + status = obx_open_l2c(p_cb, bd_addr); + } + else if (p_cb->rsp_code) /* p_cb->rsp_code is used as the scn */ + { + /* port is not open yet- open one + * this is the first CONNECT request */ + status = obx_open_port(&p_cb->ll_cb.port, bd_addr, p_cb->rsp_code); + } + } + else + { + /* when called by other OBX functions */ + status = OBX_SUCCESS; + } + + if (status == OBX_SUCCESS) + { + /* OBEX 1.5 */ + p_cb->srm = OBX_SRM_NO; + if (srm) + p_cb->srm = OBX_SRM_ENABLE; + p_cb->nonce = nonce; + p_cb->sess_st = OBX_SESS_NONE; + if (nonce) + { + if ( (p_req = OBX_HdrInit(handle, OBX_MIN_MTU)) != NULL) + { + p = (UINT8 *) (p_req + 1) + p_req->offset; + /* Session request packet always has the final bit set */ + *p++ = (OBX_REQ_SESSION | OBX_FINAL); + p_req->len = 3; + p = data; + /* add session opcode */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_OP; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_SESS_OP; + triplet[num_trip].p_array = p; + *p = OBX_SESS_OP_CREATE; + p += OBX_LEN_SESS_PARAM_SESS_OP; + num_trip++; + + /* add device addr */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_ADDR; + triplet[num_trip].len = BD_ADDR_LEN; + triplet[num_trip].p_array = p; + BTM_GetLocalDeviceAddr (p); + p += BD_ADDR_LEN; + num_trip++; + + /* add nonce 4 - 16 bytes */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NONCE; + triplet[num_trip].len = OBX_LOCAL_NONCE_SIZE; + pn = &p_cb->sess_info[OBX_SESSION_ID_SIZE]; + triplet[num_trip].p_array = pn; + UINT32_TO_BE_STREAM(pn, nonce); + num_trip++; + + /* add timeout */ + triplet[num_trip].p_array = p; + if (obx_add_timeout (&triplet[num_trip], obx_cb.sess_tout_val, &p_cb->param.sess)) + { + num_trip ++; + p = &p_cb->sess_info[OBX_SESSION_INFO_TO_IDX]; + UINT32_TO_BE_STREAM(p, obx_cb.sess_tout_val); + p_cb->param.sess.timeout = obx_cb.sess_tout_val; + } + + OBX_AddTriplet(p_req, OBX_HI_SESSION_PARAM, triplet, num_trip); + if (p_pkt) + { + /* assume that these headers are to be added to the connect req */ + p_cb->p_next_req = p_pkt; + } + /* adjust the packet len */ + p = (UINT8 *) (p_req + 1) + p_req->offset + 1; + UINT16_TO_BE_STREAM(p, p_req->len); + p_req->event = OBX_SESSION_REQ_EVT; + p_cb->sess_st = OBX_SESS_CREATE; + obx_csm_event(p_cb, OBX_SESSION_REQ_CEVT, p_req); + } + else + status = OBX_NO_RESOURCES; + } + else /* legacy */ + { + obx_ca_connect_req (p_cb, p_pkt); + } + } + if (status != OBX_SUCCESS) + { + obx_cl_free_cb(p_cb); + } + } + return status; +} + +/******************************************************************************* +** +** Function OBX_ResumeSession +** +** Description This function registers a client entity to OBEX and resumes +** a previously interrupted reliable session. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_ResumeSession (BD_ADDR bd_addr, UINT8 ssn, UINT32 offset, tOBX_HANDLE handle) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + UINT8 *p; + tOBX_CL_CB *p_cb; + BT_HDR *p_req; + tOBX_TRIPLET triplet[6]; + UINT8 data[13]; + UINT8 num_trip = 0; + UINT8 *pn; + + OBX_TRACE_API3("OBX_ResumeSession handle: 0x%x ssn:%d offset:%d", handle, ssn, offset); + p_cb = obx_cl_get_cb(handle); + + if (p_cb) + { + OBX_TRACE_DEBUG3("OBX_ResumeSession, sess_st:%d srm:0x%x, saved state:0x%x", p_cb->sess_st, + p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX], p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + if (p_cb->sess_st == OBX_SESS_SUSPENDED) + { + if ((p_req = OBX_HdrInit(OBX_HANDLE_NULL, OBX_MIN_MTU)) != NULL) + { + p = (UINT8 *) (p_req + 1) + p_req->offset; + /* Session request packet always has the final bit set */ + *p++ = (OBX_REQ_SESSION | OBX_FINAL); + p_req->len = 3; + + /* add session opcode */ + p = data; + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_OP; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_SESS_OP; + triplet[num_trip].p_array = p; + *p = OBX_SESS_OP_RESUME; + p += OBX_LEN_SESS_PARAM_SESS_OP; + num_trip++; + + /* add device addr */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_ADDR; + triplet[num_trip].len = BD_ADDR_LEN; + triplet[num_trip].p_array = p; + BTM_GetLocalDeviceAddr (p); + p += BD_ADDR_LEN; + num_trip++; + + /* add nonce 4 - 16 bytes */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NONCE; + triplet[num_trip].len = OBX_LOCAL_NONCE_SIZE; + pn = &p_cb->sess_info[OBX_SESSION_ID_SIZE]; + triplet[num_trip].p_array = pn; + num_trip++; + + /* add session id */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_ID; + triplet[num_trip].len = OBX_SESSION_ID_SIZE; + triplet[num_trip].p_array = p_cb->sess_info; + num_trip++; + + if (p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX] & OBX_SRM_ENGAGE) + { + /* add ssn */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NSEQNUM; + triplet[num_trip].len = 1; + triplet[num_trip].p_array = p; + *p++ = ssn; + num_trip++; + + if (offset) + { + /* add object offset */ + p_cb->param.sess.obj_offset = offset; + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_OBJ_OFF; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_OBJ_OFF; + triplet[num_trip].p_array = p; + UINT32_TO_BE_STREAM(p, offset); + num_trip++; + } + } + + p_cb->sess_st = OBX_SESS_RESUME; + OBX_AddTriplet(p_req, OBX_HI_SESSION_PARAM, triplet, num_trip); + /* adjust the packet len */ + p = (UINT8 *) (p_req + 1) + p_req->offset + 1; + UINT16_TO_BE_STREAM(p, p_req->len); + p_req->event = OBX_SESSION_REQ_EVT; + status = OBX_SUCCESS; + + if (p_cb->ll_cb.comm.id == 0 || p_cb->ll_cb.comm.p_send_fn == 0) + { + /* the transport is closed. open it again */ + OBX_TRACE_DEBUG2("scn: %d, psm:0x%x", p_cb->rsp_code, p_cb->psm); + p_cb->ll_cb.comm.rx_mtu = OBX_MAX_MTU; + + if (p_cb->psm) + { + /* L2CAP channel is not open yet- open one + * this is the first CONNECT request */ + status = obx_open_l2c(p_cb, bd_addr); + } + else if (p_cb->rsp_code) /* p_cb->rsp_code is used as the scn */ + { + /* port is not open yet- open one + * this is the first CONNECT request */ + status = obx_open_port(&p_cb->ll_cb.port, bd_addr, p_cb->rsp_code); + } + } + + if (status == OBX_SUCCESS) + { + pn = &p_cb->sess_info[OBX_SESSION_INFO_ID_IDX]; + BE_STREAM_TO_UINT32(p_cb->conn_id, pn); + p_cb->ssn = ssn; + p_cb->param.sess.ssn = ssn; + obx_csm_event(p_cb, OBX_SESSION_REQ_CEVT, p_req); + } + } + } + else + { + OBX_TRACE_ERROR1("Handle is not in a right state: %d for RESUME", p_cb->sess_st); + status = OBX_BAD_HANDLE; + } + } + return status; +} + +/******************************************************************************* +** +** Function OBX_SessionReq +** +** Description This function is used to Suspend/Close a session or update the +** timeout value of the session. +** If timeout is 0, OBX_SESS_TIMEOUT_VALUE is the value used +** THe timeout value is not added for the CloseSession request +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_SessionReq (tOBX_HANDLE handle, tOBX_SESS_OP opcode, UINT32 timeout) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + UINT8 *p; + tOBX_CL_CB *p_cb; + BT_HDR *p_req; + tOBX_TRIPLET triplet[3]; + UINT8 data[12]; + UINT8 num_trip = 0; + tOBX_SESS_ST old_sess_st; + + p_cb = obx_cl_get_cb(handle); + + if (p_cb) + { + OBX_TRACE_API2("OBX_SessionReq st:%d opcode:%d", p_cb->sess_st, opcode); + old_sess_st = p_cb->sess_st; + if ((p_req = OBX_HdrInit(OBX_HANDLE_NULL, OBX_MIN_MTU)) != NULL) + { + status = OBX_SUCCESS; + p = (UINT8 *) (p_req + 1) + p_req->offset; + /* Session request packet always has the final bit set */ + *p++ = (OBX_REQ_SESSION | OBX_FINAL); + p_req->len = 3; + + /* add session opcode */ + p = data; + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_OP; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_SESS_OP; + triplet[num_trip].p_array = p; + *p = opcode; + p += OBX_LEN_SESS_PARAM_SESS_OP; + num_trip++; + if (timeout == 0) + { + timeout = obx_cb.sess_tout_val; + if (p_cb->srm & OBX_SRM_ENABLE) + timeout += obx_cb.sess_tout_val; + } + triplet[num_trip].p_array = p; + switch (opcode) + { + case OBX_SESS_OP_CLOSE: + /* do not need any other session parameters */ + if (p_cb->sess_st != OBX_SESS_NONE) + p_cb->sess_st = OBX_SESS_CLOSE; + else + { + OBX_TRACE_ERROR1("Handle is not in a right state: %d for CLOSE", p_cb->sess_st); + status = OBX_BAD_HANDLE; + } + break; + + case OBX_SESS_OP_SUSPEND: + /* do not need any other session parameters */ + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + /* add timeout value */ + num_trip += obx_add_timeout (&triplet[num_trip], timeout, &p_cb->param.sess); + p_cb->sess_st = OBX_SESS_SUSPEND; + } + else + { + OBX_TRACE_ERROR1("Handle is not in a right state: %d for SUSPEND", p_cb->sess_st); + status = OBX_BAD_HANDLE; + } + break; + + case OBX_SESS_OP_SET_TIME: + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + /* add timeout value */ + num_trip += obx_add_timeout (&triplet[num_trip], timeout, &p_cb->param.sess); + p_cb->sess_st = OBX_SESS_TIMEOUT; + } + else + { + OBX_TRACE_ERROR1("Handle is not in a right state: %d for SET_TIME", p_cb->sess_st); + status = OBX_BAD_HANDLE; + } + break; + default: + OBX_TRACE_ERROR1("bad session opcode :%d", opcode); + status = OBX_BAD_PARAMS; + } + + OBX_TRACE_DEBUG4("OBX_SessionReq, sess_st:%d->%d opcode:%d status:%d", old_sess_st, p_cb->sess_st, opcode, status); + if (status != OBX_SUCCESS) + GKI_freebuf (p_req); + else + { + OBX_AddTriplet(p_req, OBX_HI_SESSION_PARAM, triplet, num_trip); + if (p_cb->sess_st == OBX_SESS_SUSPEND) + { + p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_cb->state; + if (p_cb->state == OBX_CS_PARTIAL_SENT) + p_cb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_cb->prev_state; + p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_cb->srm; + OBX_TRACE_DEBUG2("suspend saved st:%d, srm:0x%x", p_cb->sess_info[OBX_SESSION_INFO_ST_IDX], p_cb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + } + /* adjust the packet len */ + p = (UINT8 *) (p_req + 1) + p_req->offset + 1; + UINT16_TO_BE_STREAM(p, p_req->len); + p_req->event = OBX_SESSION_REQ_EVT; + obx_csm_event(p_cb, OBX_SESSION_REQ_CEVT, p_req); + } + } + } + return status; +} + +/******************************************************************************* +** +** Function OBX_GetPortHandle +** +** Description This function is called to get RFCOMM port handle for the obex connection. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if no existing connection. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_GetPortHandle(tOBX_HANDLE handle, UINT16 *port_handle) +{ + BD_ADDR bd_addr; + UINT16 lcid; + tOBX_STATUS status = OBX_SUCCESS; + tOBX_CL_CB *p_cb = obx_cl_get_cb(handle); + + if (p_cb) + { + if (PORT_CheckConnection(p_cb->ll_cb.port.port_handle, bd_addr, &lcid) != PORT_SUCCESS) + { + status = OBX_NO_RESOURCES; + } + else + { + *port_handle = p_cb->ll_cb.port.port_handle; + } + } + else + status = OBX_BAD_HANDLE; + + return status; +} + +/******************************************************************************* +** +** Function OBX_SetPathReq +** +** Description This function sends a Set Path request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_SetPathReq(tOBX_HANDLE handle, UINT8 flags, BT_HDR *p_pkt) +{ + tOBX_CL_CB *p_cb = obx_cl_get_cb(handle); + tOBX_STATUS status = OBX_BAD_HANDLE; + UINT8 msg[OBX_HDR_OFFSET]; + UINT8 *p = msg; + UINT8 good_flags = (OBX_SPF_BACKUP | OBX_SPF_NO_CREATE); + + if (p_cb) + { + /* SetPath request packet always has the final bit set */ + *p++ = (OBX_REQ_SETPATH | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + *p++ = (flags & good_flags); /* send only good flags */ + *p++ = OBX_SETPATH_CONST; + + /* add session sequence number, if session is active */ + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = p_cb->ssn; + } + + /* add connection ID, if needed */ + if (p_cb->conn_id != OBX_INVALID_CONN_ID) + { + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_cb->conn_id); + } + p_pkt = obx_cl_prepend_msg(p_cb, p_pkt, msg, (UINT16)(p - msg) ); + p_pkt->event = OBX_SETPATH_REQ_EVT; + obx_csm_event(p_cb, OBX_SETPATH_REQ_CEVT, p_pkt); + status = OBX_SUCCESS; + } + return status; +} + +/******************************************************************************* +** +** Function obx_prepend_req_msg +** +** Description This function is called to add request code and connection ID +** to the given OBEX message +** Returns void +** +*******************************************************************************/ +tOBX_STATUS obx_prepend_req_msg(tOBX_HANDLE handle, tOBX_CL_EVENT event, UINT8 req_code, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_CL_CB *p_cb = obx_cl_get_cb(handle); + UINT8 msg[OBX_HDR_OFFSET]; + UINT8 *p = msg; + UINT8 srm = 0; + UINT8 num_hdrs, num_body; + + if (p_cb) + { + *p++ = req_code; + p += OBX_PKT_LEN_SIZE; + + /* add session sequence number, if session is active */ + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = p_cb->ssn; + } + + req_code &= ~OBX_FINAL; + + /* add connection ID, if needed */ + if ((p_cb->conn_id != OBX_INVALID_CONN_ID) && + /* always use connection ID in CONNECTED state or being challenged on operation */ + ((p_cb->state == OBX_CS_CONNECTED) || (p_cb->state == OBX_CS_OP_UNAUTH) || + /* always use connection ID for abort and disconnect. they may be out of sequence */ + (req_code == OBX_REQ_ABORT) || (req_code == OBX_REQ_DISCONNECT))) + { + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_cb->conn_id); + } + + /* add SRM header, if SRM is enabled */ + if (p_cb->srm & OBX_SRM_ENABLE) + { + if (p_cb->state == OBX_CS_CONNECTED) + { + if(event == OBX_PUT_REQ_CEVT) + { + num_hdrs = OBX_ReadNumHdrs(p_pkt, &num_body); + OBX_TRACE_DEBUG2("num_hdrs:%d num_body:%d", num_hdrs, num_body); + if (num_hdrs == num_body) + { + OBX_TRACE_DEBUG0("it is left-over, drop it"); + if (p_pkt) + GKI_freebuf (p_pkt); + return OBX_BAD_PARAMS; + } + srm = OBX_SRM_REQING | OBX_SRM_WAIT; + } + else if (event == OBX_GET_REQ_CEVT) + { + srm = OBX_SRM_REQING; + } + + OBX_TRACE_DEBUG2("cb srm: 0x%x/0x%x", p_cb->srm, srm ); + if (srm) + { + p_cb->srm |= srm; + *p++ = OBX_HI_SRM; + *p++ = OBX_HV_SRM_ENABLE; + } + } + } + + p_pkt = obx_cl_prepend_msg(p_cb, p_pkt, msg, (UINT16)(p - msg) ); + p_pkt->event = obx_sm_evt_to_api_evt[event]; + obx_csm_event(p_cb, event, p_pkt); + } + else + status = OBX_BAD_HANDLE; + + return status; +} + +/******************************************************************************* +** +** Function OBX_PutReq +** +** Description This function sends a Put request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_PutReq(tOBX_HANDLE handle, BOOLEAN final, BT_HDR *p_pkt) +{ + UINT8 req_code = OBX_REQ_PUT; + if (final) + req_code |= OBX_FINAL; + return obx_prepend_req_msg(handle, OBX_PUT_REQ_CEVT, req_code, p_pkt); + +} + + +/******************************************************************************* +** +** Function OBX_GetReq +** +** Description This function sends a Get request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_GetReq(tOBX_HANDLE handle, BOOLEAN final, BT_HDR *p_pkt) +{ + UINT8 req_code = OBX_REQ_GET; + OBX_TRACE_API1("OBX_GetReq final: 0x%x", final ); + if (final) + req_code |= OBX_FINAL; + return obx_prepend_req_msg(handle, OBX_GET_REQ_CEVT, req_code, p_pkt); +} + + +/******************************************************************************* +** +** Function OBX_AbortReq +** +** Description This function sends an Abort request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_AbortReq(tOBX_HANDLE handle, BT_HDR *p_pkt) +{ + /* Disconnect request always has the final bit set */ + UINT8 req_code = (OBX_REQ_ABORT|OBX_FINAL); + return obx_prepend_req_msg(handle, OBX_ABORT_REQ_CEVT, req_code, p_pkt); +} + + +/******************************************************************************* +** +** Function OBX_DisconnectReq +** +** Description This function sends a Disconnect request to the connected server. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_DisconnectReq(tOBX_HANDLE handle, BT_HDR *p_pkt) +{ + /* Disconnect request always has the final bit set */ + UINT8 req_code = (OBX_REQ_DISCONNECT|OBX_FINAL); + return obx_prepend_req_msg(handle, OBX_DISCNT_REQ_CEVT, req_code, p_pkt); +} + +/******************************************************************************* +** +** Function OBX_ActionReq +** +** Description This function sends a Action request to the connected server. +** The Name Header and DestName Header must be in p_pkt for +** the Copy and Move Object action. +** The Name header and Permission Header must be in p_pkt for +** the Set Object Permission action. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_ActionReq(tOBX_HANDLE handle, tOBX_ACTION action_id, BT_HDR *p_pkt) +{ + /* Disconnect request always has the final bit set */ + UINT8 req_code = (OBX_REQ_ACTION|OBX_FINAL); + UINT8 *p; + + if (p_pkt == NULL) + { + OBX_TRACE_ERROR0("OBX_ActionReq must include Name & DestName Header for Copy/Move action" ); + OBX_TRACE_ERROR0("OBX_ActionReq must include Name & Permission Header for Set Object Permission action" ); + return OBX_BAD_PARAMS; + } + + /* add the Action ID header before the other headers */ + p_pkt->offset -= 2; + p_pkt->len += 2; + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + *p++ = OBX_HI_ACTION_ID; + *p++ = action_id; + + return obx_prepend_req_msg(handle, OBX_ACTION_REQ_CEVT, req_code, p_pkt); +} diff --git a/stack/obx/obx_csm.c b/stack/obx/obx_csm.c new file mode 100644 index 0000000..25447df --- /dev/null +++ b/stack/obx/obx_csm.c @@ -0,0 +1,365 @@ +/***************************************************************************** +** +** Name: obx_csm.c +** +** File: OBEX Client State Machine and Control Block Access Functions +** +** Copyright (c) 2003-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "bt_target.h" +#include "btu.h" /* for timer */ +#include "obx_int.h" + +/* OBEX Client Action Functions Enums (must match obx_cl_action [below] */ +enum +{ + OBX_CA_SND_REQ, + OBX_CA_NOTIFY, + OBX_CA_CONNECT_ERROR, + OBX_CA_STATE, + OBX_CA_CLOSE_PORT, + OBX_CA_CONNECT_FAIL, + OBX_CA_DISCNT_REQ, + OBX_CA_START_TIMER, + OBX_CA_FAIL_RSP, + OBX_CA_SND_PART, + OBX_CA_CONNECT_OK, + OBX_CA_SESSION_OK, + OBX_CA_SESSION_CONT, + OBX_CA_SESSION_GET, + OBX_CA_SESSION_FAIL, + OBX_CA_ABORT, + OBX_CA_SND_PUT_REQ, + OBX_CA_SND_GET_REQ, + OBX_CA_SRM_SND_REQ, + OBX_CA_SRM_PUT_REQ, + OBX_CA_SRM_GET_REQ, + OBX_CA_SRM_PUT_NOTIFY, + OBX_CA_SRM_GET_NOTIFY, + OBX_CA_SAVE_RSP, + OBX_CA_SAVE_REQ +}; + +/* OBEX Client Action Functions */ +static const tOBX_CL_ACT obx_cl_action[] = +{ + obx_ca_snd_req, + obx_ca_notify, + obx_ca_connect_error, + obx_ca_state, + obx_ca_close_port, + obx_ca_connect_fail, + obx_ca_discnt_req, + obx_ca_start_timer, + obx_ca_fail_rsp, + obx_ca_snd_part, + obx_ca_connect_ok, + obx_ca_session_ok, + obx_ca_session_cont, + obx_ca_session_get, + obx_ca_session_fail, + obx_ca_abort, + obx_ca_snd_put_req, + obx_ca_snd_get_req, + obx_ca_srm_snd_req, + obx_ca_srm_put_req, + obx_ca_srm_get_req, + obx_ca_srm_put_notify, + obx_ca_srm_get_notify, + obx_ca_save_rsp, + obx_ca_save_req +}; + +/************ OBX Client FSM State/Event Indirection Table **************/ +/* obx_csm_event() first looks at obx_csm_entry_map[][] to get an entry of the event of a particular state + * 0 means the event in the current state is ignored. + * a number with 0x80 bit set, use obx_cl_all_table[][] as the "state table". + * other numbers, look up obx_cl_main_state_table[] for the state table of current state. + * + * once the state table is determined, + * look up the "action" column to find the associated action function + * and the "next state" column to find the "next state" candidate. + * + * The actual next state could be either the state in the "next state" column + * or the state returned from the action function. + */ +static const UINT8 obx_csm_entry_map[][OBX_CS_MAX-1] = +{ +/* state name: NtCon SesRs ConRs UnAut Conn DscRs OpUna StpRs ActRs AbtRs PutRs GetRs Put Get PutS GetS Part */ +/* CONN_R */{ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* SESS_R */{ 2, 0, 0, 0, 5, 0, 0, 0, 0, 0, 2, 2, 2, 2, 5, 5, 1 }, +/* DISCNT_R */{ 0, 0x87, 2, 0x87, 0x81, 0x87, 0x87, 0x81, 0x81, 0x81, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87 }, +/* PUT_R */{ 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0 }, +/* GET_R */{ 0, 3, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0 }, +/* SETPATH_R*/{ 0, 0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* ACT_R */{ 0, 0, 0, 0, 6, 0, 5, 0, 0, 0, 0x82, 0x82, 0x82, 0x82, 3, 3, 1 }, +/* ABORT_R */{ 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0x82, 0x82, 0x82, 0x82, 3, 3, 1 }, +/* OK_C */{ 0, 1, 3, 0, 0, 1, 0, 0x83, 0x83, 0x83, 0x83, 0x83, 0, 0, 0x83, 0x83, 3 }, +/* CONT_C */{ 0, 2, 0, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 0, 4, 4, 3 }, +/* FAIL_C */{ 0, 4, 1, 0, 0, 0x87, 0, 0x86, 0x86, 2, 0x86, 0x86, 0, 0, 0x86, 0x86, 3 }, +/* PORT_CLS */{ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84 }, +/* TX_EMPTY */{ 0x87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/* FCS_SET */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }, +/* STATE */{ 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85 }, +/* TIMEOUT */{ 3, 2, 0x87, 0, 0, 0x87, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static const UINT8 obx_cl_all_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* DISCNT_R */{OBX_CA_SND_REQ, OBX_CS_DISCNT_REQ_SENT }, +/* ABORT_R */{OBX_CA_SND_REQ, OBX_CS_ABORT_REQ_SENT }, +/* OK_C */{OBX_CA_NOTIFY, OBX_CS_NULL }, +/* PORT_CLS */{OBX_CA_CONNECT_ERROR, OBX_CS_NOT_CONNECTED }, +/* STATE */{OBX_CA_STATE, OBX_CS_NULL }, +/* FAIL_C */{OBX_CA_FAIL_RSP, OBX_CS_NULL }, +/* end */{OBX_CA_CLOSE_PORT, OBX_CS_NOT_CONNECTED } +}; + +static const UINT8 obx_cl_not_conn_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_R */{OBX_CA_SND_REQ, OBX_CS_CONNECT_REQ_SENT }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT }, +/* TIMEOUT */{OBX_CA_SESSION_FAIL, OBX_CS_NOT_CONNECTED } +}; + +static const UINT8 obx_cl_sess_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* OK_C */{OBX_CA_SESSION_OK, OBX_CS_NOT_CONNECTED }, +/* CONT_C */{OBX_CA_SESSION_CONT, OBX_CS_SESSION_REQ_SENT }, +/* GET_R */{OBX_CA_SESSION_GET, OBX_CS_SESSION_REQ_SENT }, +/* FAIL_C */{OBX_CA_SESSION_FAIL, OBX_CS_NOT_CONNECTED } +}; + +static const UINT8 obx_cl_conn_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* FAIL_C */{OBX_CA_CONNECT_FAIL, OBX_CS_UNAUTH }, +/* DISCNT_R */{OBX_CA_DISCNT_REQ, OBX_CS_NULL }, +/* OK_C */{OBX_CA_CONNECT_OK, OBX_CS_NULL } +}; + +static const UINT8 obx_cl_unauth_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_R */{OBX_CA_SND_REQ, OBX_CS_CONNECT_REQ_SENT } +}; + +static const UINT8 obx_cl_conn_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_CA_SND_PUT_REQ, OBX_CS_PUT_REQ_SENT }, +/* GET_R */{OBX_CA_SND_GET_REQ, OBX_CS_GET_REQ_SENT }, +/* SETPATH_R*/{OBX_CA_SND_REQ, OBX_CS_SETPATH_REQ_SENT }, +/* ABORT_R */{OBX_CA_ABORT, OBX_CS_CONNECTED }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT }, +/* ACT_R */{OBX_CA_SND_REQ, OBX_CS_ACTION_REQ_SENT } +}; + +static const UINT8 obx_cl_discnt_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* OK_C */{OBX_CA_NOTIFY, OBX_CS_NOT_CONNECTED }, /* and close port */ +/* CONT_C */{OBX_CA_START_TIMER, OBX_CS_DISCNT_REQ_SENT } +}; + +static const UINT8 obx_cl_op_unauth_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_CA_SND_REQ, OBX_CS_PUT_REQ_SENT }, +/* GET_R */{OBX_CA_SND_REQ, OBX_CS_GET_REQ_SENT }, +/* SETPATH_R*/{OBX_CA_SND_REQ, OBX_CS_SETPATH_REQ_SENT }, +/* ABORT_R */{OBX_SM_NO_ACTION, OBX_CS_CONNECTED }, +/* ACT_R */{OBX_CA_SND_REQ, OBX_CS_ACTION_REQ_SENT } +}; + +/* static const UINT8 obx_cl_setpath_rs_table[][OBX_SM_NUM_COLS] = { */ +/* Event Action Next State */ +/* DISCNT_R {OBX_CA_SND_REQ, OBX_CS_DISCNT_REQ_SENT },*/ +/* OK_C {OBX_CA_NOTIFY, OBX_CS_NULL },*/ +/* FAIL_C {OBX_CA_FAIL_RSP, OBX_CS_NULL },*/ +/* PORT_CLS {OBX_CA_CONNECT_ERROR, OBX_CS_NOT_CONNECTED },*/ +/* STATE {OBX_CA_STATE, OBX_CS_NULL },*/ +/* }; */ + +static const UINT8 obx_cl_abort_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONT_C */{OBX_CA_START_TIMER, OBX_CS_ABORT_REQ_SENT }, +/* FAIL_C */{OBX_CA_NOTIFY, OBX_CS_CONNECTED } +}; + +static const UINT8 obx_cl_put_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONT_C */{OBX_CA_NOTIFY, OBX_CS_PUT_TRANSACTION }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_get_rs_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONT_C */{OBX_CA_NOTIFY, OBX_CS_GET_TRANSACTION }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_put_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_CA_SND_REQ, OBX_CS_PUT_REQ_SENT }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_get_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* GET_R */{OBX_CA_SND_REQ, OBX_CS_GET_REQ_SENT }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_put_s_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* DISCNT_R */{OBX_CA_SRM_SND_REQ, OBX_CS_DISCNT_REQ_SENT }, +/* PUT_R */{OBX_CA_SRM_PUT_REQ, OBX_CS_PUT_SRM }, +/* ABORT_R */{OBX_CA_SRM_SND_REQ, OBX_CS_ABORT_REQ_SENT }, +/* CONT_C */{OBX_CA_SRM_PUT_NOTIFY, OBX_CS_PUT_SRM }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_get_s_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* DISCNT_R */{OBX_CA_SRM_SND_REQ, OBX_CS_DISCNT_REQ_SENT }, +/* GET_R */{OBX_CA_SRM_GET_REQ, OBX_CS_GET_SRM }, +/* ABORT_R */{OBX_CA_SRM_SND_REQ, OBX_CS_ABORT_REQ_SENT }, +/* CONT_C */{OBX_CA_SRM_GET_NOTIFY, OBX_CS_GET_SRM }, +/* SESS_R */{OBX_CA_SND_REQ, OBX_CS_SESSION_REQ_SENT } +}; + +static const UINT8 obx_cl_part_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* ABORT_R */{OBX_CA_SAVE_REQ, OBX_CS_PARTIAL_SENT }, +/* FCS_SET */{OBX_CA_SND_PART, OBX_CS_NULL }, +/* FAIL_C */{OBX_CA_SAVE_RSP, OBX_CS_NULL } +}; + +static const tOBX_SM_TBL obx_cl_main_state_table[] = { + obx_cl_not_conn_table, + obx_cl_sess_rs_table, + obx_cl_conn_rs_table, + obx_cl_unauth_table, + obx_cl_conn_table, + obx_cl_discnt_rs_table, + obx_cl_op_unauth_table, + NULL, /* obx_cl_setpath_rs_table */ + NULL, /* obx_cl_action_rs_table */ + obx_cl_abort_rs_table, + obx_cl_put_rs_table, + obx_cl_get_rs_table, + obx_cl_put_table, + obx_cl_get_table, + obx_cl_put_s_table, + obx_cl_get_s_table, + obx_cl_part_table +}; + +/******************************************************************************* +** +** Function obx_csm_event +** +** Description Handle events to the client state machine. It looks up the entry +** in the obx_csm_entry_map array. If it is a valid entry, it gets +** the state table. Set the next state, if not NULL state.Execute +** the action function according to the state table. If the state +** returned by action function is not NULL state, adjust the new +** state to the returned state.If (api_evt != MAX), call callback +** function. +** +** Returns void. +** +*******************************************************************************/ +void obx_csm_event(tOBX_CL_CB *p_cb, tOBX_CL_EVENT event, BT_HDR *p_msg) +{ + UINT8 curr_state = p_cb->state; + tOBX_SM_TBL state_table = NULL; + UINT8 action, entry; + tOBX_CL_STATE act_state = OBX_CS_NULL; + UINT8 prev_state = OBX_CS_NULL; + + if( curr_state == OBX_CS_NULL || curr_state >= OBX_CS_MAX) + { + OBX_TRACE_WARNING1( "Invalid state: %d", curr_state) ; + if(p_msg) + GKI_freebuf(p_msg); + return; + } + OBX_TRACE_DEBUG4( "Client Handle 0x%x, State: %s, event: %s srm:0x%x", + p_cb->ll_cb.comm.handle, obx_cl_get_state_name( p_cb->state ), obx_cl_get_event_name(event), p_cb->srm ) ; + OBX_TRACE_DEBUG1("obx_csm_event csm offset:%d", p_cb->param.sess.obj_offset); + + /* look up the state table for the current state */ + /* lookup entry /w event & curr_state */ + /* If entry is ignore, return. + * Otherwise, get state table (according to curr_state or all_state) */ + if( (entry = obx_csm_entry_map[event][curr_state-1]) != OBX_SM_IGNORE ) + { + if(entry&OBX_SM_ALL) + { + entry &= OBX_SM_ENTRY_MASK; + state_table = obx_cl_all_table; + } + else + state_table = obx_cl_main_state_table[curr_state-1]; + } + + if( entry == OBX_SM_IGNORE || state_table == NULL) + { + OBX_TRACE_WARNING4( "Ignore event %s(%d) in state %s(%d)", + obx_cl_get_event_name(event), event, obx_cl_get_state_name(curr_state), curr_state ); + if(p_msg) + GKI_freebuf(p_msg); + return; + } + + /* Get possible next state from state table. */ + if( state_table[entry-1][OBX_SME_NEXT_STATE] != OBX_CS_NULL ) + { + prev_state = p_cb->state; + p_cb->state = state_table[entry-1][OBX_SME_NEXT_STATE]; + if (prev_state != p_cb->state) + { + p_cb->prev_state = prev_state; + OBX_TRACE_DEBUG1( "saved state1:%s", obx_cl_get_state_name(p_cb->prev_state)); + } + } + OBX_TRACE_DEBUG1( "possible new state = %s", obx_cl_get_state_name( p_cb->state ) ) ; + + /* If action is not ignore, clear param, exec action and get next state. + * The action function may set the Param for cback. + * Depending on param, call cback or free buffer. */ + /* execute action */ + action = state_table[entry-1][OBX_SME_ACTION]; + if (action != OBX_SM_NO_ACTION) + { + act_state = (*obx_cl_action[action])(p_cb, p_msg); + } + + /* adjust next state, if it needs to use the new state returned from action function */ + if( act_state != OBX_CS_NULL) + { + prev_state = p_cb->state; + p_cb->state = act_state; + OBX_TRACE_DEBUG1( "new state = %s (action)", obx_cl_get_state_name( p_cb->state )) ; + if (prev_state != p_cb->state) + { + p_cb->prev_state = prev_state; + OBX_TRACE_DEBUG1( "saved state2:%s", obx_cl_get_state_name(p_cb->prev_state)); + } + } + + if(p_cb->api_evt) + { + (p_cb->p_cback) (p_cb->ll_cb.comm.handle, p_cb->api_evt, p_cb->rsp_code, p_cb->param, p_msg); + p_cb->api_evt = OBX_NULL_EVT; + p_cb->rsp_code = 0; + memset(&p_cb->param, 0, sizeof (p_cb->param) ); + } + else if(action == OBX_SM_NO_ACTION && p_msg) + GKI_freebuf(p_msg); + OBX_TRACE_DEBUG1("after csm offset:%d", p_cb->param.sess.obj_offset); + + OBX_TRACE_DEBUG2( "result state = %s/%d", obx_cl_get_state_name( p_cb->state ), p_cb->state ) ; +} + + diff --git a/stack/obx/obx_int.h b/stack/obx/obx_int.h new file mode 100644 index 0000000..8d4a7f6 --- /dev/null +++ b/stack/obx/obx_int.h @@ -0,0 +1,602 @@ +/***************************************************************************** +** +** Name: obx_int.h +** +** File: OBEX Internal header file +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#ifndef OBX_INT_H +#define OBX_INT_H + +#include "bt_target.h" +#include "obx_api.h" +#include "gki.h" + +#define OBX_DEFAULT_TARGET_LEN 0xFF +#define OBX_INITIAL_CONN_ID 0x1 + +/* the rules for OBX handles: (some of the definitions are in obx_api.h) + * + * tOBX_HANDLE is UINT16 + * It was UINT8, the support for multiple clients on the same SCN for MAP is required + * + * LSB (0x00FF) is the same as the old definition. + * The 0x80 bit (OBX_CL_HANDLE_MASK) is set for client connections. + * The 0x40 bit (OBX_HANDLE_RX_MTU_MASK) is used internally for RFCOMM to allocate a buffer to receive data + * + * The MSB (0xFF00) is used for enhancements add for BTE release 3.15 + * This byte is the session index; used for server only. + */ + +#define OBX_CL_HANDLE_MASK 0x80 +#define OBX_CL_CB_IND_MASK 0x007F +#define OBX_HANDLE_RX_MTU_MASK 0x40 +#define OBX_LOCAL_NONCE_SIZE OBX_MIN_NONCE_SIZE /* 4 - 16 per IrOBEX spec. local nonce is always 4 */ + +#define OBX_MAX_EVT_MAP_NUM (OBX_ABORT_REQ_SEVT+1) + +#define OBX_PORT_EVENT_MASK (PORT_EV_RXCHAR | PORT_EV_TXEMPTY | \ + PORT_EV_FC | PORT_EV_FCS) + +#define OBX_BAD_SM_EVT 0xFF + +/* for wait_auth flag */ +#define OBX_WAIT_AUTH_FAIL 2 + +enum +{ + OBX_CS_NULL, /* 0 0 */ + OBX_CS_NOT_CONNECTED, /* 1 1 */ + OBX_CS_SESSION_REQ_SENT, /* 2 */ + OBX_CS_CONNECT_REQ_SENT, /* 3 2 */ + OBX_CS_UNAUTH, /* 4 3 */ + OBX_CS_CONNECTED, /* 5 4 */ + OBX_CS_DISCNT_REQ_SENT, /* 6 5 */ + OBX_CS_OP_UNAUTH, /* 7 6 */ + OBX_CS_SETPATH_REQ_SENT, /* 8 7 */ + OBX_CS_ACTION_REQ_SENT, /* 9 */ + OBX_CS_ABORT_REQ_SENT, /*10 8 */ + OBX_CS_PUT_REQ_SENT, /*11 9 */ + OBX_CS_GET_REQ_SENT, /*12 10 */ + OBX_CS_PUT_TRANSACTION, /*13 11 */ + OBX_CS_GET_TRANSACTION, /*14 12 */ + OBX_CS_PUT_SRM, /*15 */ + OBX_CS_GET_SRM, /*16 */ + OBX_CS_PARTIAL_SENT, /*17 13 */ + OBX_CS_MAX +}; + +typedef UINT8 tOBX_CL_STATE; +#define OBX_CL_STATE_DROP 0x80 /* this is used only in session_info[] to mark link drop suspend*/ + +enum +{ + OBX_CONNECT_REQ_CEVT, /* API call to send a CONNECT request. */ + OBX_SESSION_REQ_CEVT, /* API call to send a SESSION request. */ + OBX_DISCNT_REQ_CEVT, /* API call to send a DISCONNECT request. */ + OBX_PUT_REQ_CEVT, /* API call to send a PUT request. */ + OBX_GET_REQ_CEVT, /* API call to send a GET request.*/ + OBX_SETPATH_REQ_CEVT, /* API call to send a SETPATH request. */ + OBX_ACTION_REQ_CEVT, /* API call to send an ACTION request. */ + OBX_ABORT_REQ_CEVT, /* API call to send an ABORT request. */ + OBX_OK_CFM_CEVT, /* Received success response from server. */ + OBX_CONT_CFM_CEVT, /* Received continue response from server. */ + OBX_FAIL_CFM_CEVT, /* Received failure response from server. */ + OBX_PORT_CLOSE_CEVT, /* Transport is closed. */ + OBX_TX_EMPTY_CEVT, /* Transmit Queue Empty */ + OBX_FCS_SET_CEVT, /* Data flow enable */ + OBX_STATE_CEVT, /* Change state. */ + OBX_TIMEOUT_CEVT, /* Timeout occurred. */ + OBX_MAX_CEVT +}; +typedef UINT8 tOBX_CL_EVENT; + +#define OBX_MAX_API_CEVT OBX_ABORT_REQ_CEVT + +enum +{ + OBX_CONNECT_REQ_SEVT, /* 1 1 Received CONNECT request from client. */ + OBX_SESSION_REQ_SEVT, /* 2 Received SESSION request from client. */ + OBX_DISCNT_REQ_SEVT, /* 3 2 Received DISCONNECT request from client. */ + OBX_PUT_REQ_SEVT, /* 4 3 Received PUT request from client. */ + OBX_GET_REQ_SEVT, /* 5 4 Received GET request from client. */ + OBX_SETPATH_REQ_SEVT, /* 6 5 Received SETPATH request from client. */ + OBX_ACTION_REQ_SEVT, /* 7 Received ACTION request from client. */ + OBX_ABORT_REQ_SEVT, /* 8 6 Received ABORT request from client. */ + OBX_CONNECT_CFM_SEVT, /* 9 7 API call to send a CONNECT response. */ + OBX_SESSION_CFM_SEVT, /*10 API call to send a SESSION response. */ + OBX_DISCNT_CFM_SEVT, /*11 8 API call to send a DISCONNECT response or close the connection to the client. */ + OBX_PUT_CFM_SEVT, /*12 9 API call to send a PUT response. */ + OBX_GET_CFM_SEVT, /*13 10 API call to send a GET response. */ + OBX_SETPATH_CFM_SEVT, /*14 11 API call to send a SETPATH response. */ + OBX_ACTION_CFM_SEVT, /*15 API call to send an ACTION response. */ + OBX_ABORT_CFM_SEVT, /*16 12 API call to send an ABORT response. */ + OBX_PORT_CLOSE_SEVT, /*17 13 Transport is closed. */ + OBX_FCS_SET_SEVT, /*18 14 Data flow enable */ + OBX_STATE_SEVT, /*19 15 Change state. */ + OBX_TIMEOUT_SEVT, /*20 16 Timeout has occurred. */ + OBX_BAD_REQ_SEVT, /*21 17 Received a bad request from client. */ + OBX_TX_EMPTY_SEVT, /*22 18 Transmit Queue Empty */ + OBX_MAX_SEVT +}; +typedef UINT8 tOBX_SR_EVENT; + +#define OBX_SEVT_DIFF_REQ_CFM (OBX_CONNECT_CFM_SEVT - OBX_CONNECT_REQ_SEVT) /* the index difference between *REQ_SEVT and *CFM_SEVT */ +#define OBX_SEVT_MAX_REQ OBX_ACTION_REQ_SEVT /* last *REQ_SEVT */ + +enum +{ + OBX_SS_NULL, /* 0 0 */ + OBX_SS_NOT_CONNECTED, /* 1 1 */ + OBX_SS_SESS_INDICATED, /* 2 */ + OBX_SS_CONN_INDICATED, /* 3 2 */ + OBX_SS_WAIT_AUTH, /* 4 3 */ + OBX_SS_AUTH_INDICATED, /* 5 4 */ + OBX_SS_CONNECTED, /* 6 5 */ + OBX_SS_DISCNT_INDICATED, /* 7 6 */ + OBX_SS_SETPATH_INDICATED, /* 8 7 */ + OBX_SS_ACTION_INDICATED, /* 9 */ + OBX_SS_ABORT_INDICATED, /*10 8 */ + OBX_SS_PUT_INDICATED, /*11 9 */ + OBX_SS_GET_INDICATED, /*12 10 */ + OBX_SS_PUT_TRANSACTION, /*13 11 */ + OBX_SS_GET_TRANSACTION, /*14 12 */ + OBX_SS_PUT_SRM, /*15 */ + OBX_SS_GET_SRM, /*16 */ + OBX_SS_PARTIAL_SENT, /*17 13 */ + OBX_SS_WAIT_CLOSE, /*18 14 */ + OBX_SS_MAX +}; +typedef UINT8 tOBX_SR_STATE; + +typedef UINT8 tOBX_STATE; /* this must be the same type as tOBX_SR_STATE and tOBX_CL_STATE */ + +typedef struct +{ + UINT16 pkt_len;/* the packet length */ + UINT8 code; /* the response/request code with the final bit */ + UINT8 sm_evt; /* The state machine event*/ +} tOBX_RX_HDR; + +typedef void (tOBX_CLOSE_FN) (UINT16); +typedef BOOLEAN (tOBX_SEND_FN) (void *p_cb); +typedef UINT8 (*tOBX_VERIFY_OPCODE)(UINT8 opcode, tOBX_RX_HDR *p_rxh); + +#define OBX_SRM_NO 0x00 /* SRM is not enabled and not engaged */ +#define OBX_SRM_ENABLE 0x01 /* SRM is enabled. */ +#define OBX_SRM_PARAM_AL 0x02 /* SRMP is allowed only in the beginning of a transaction */ +#define OBX_SRM_REQING 0x04 /* requesting/requested to enable SRM. */ +#define OBX_SRM_ABORT 0x08 /* (server) abort/reject at SRM GET/PUT rsp API*/ +#define OBX_SRM_ENGAGE 0x10 /* SRM is engaged. */ +#define OBX_SRM_NEXT 0x20 /* peer is ready for next packet. */ +#define OBX_SRM_WAIT 0x40 /* wait for peer. */ +#define OBX_SRM_WAIT_UL 0x80 /* wait for upper layer. */ +typedef UINT8 tOBX_SRM; + +#define OBX_SRMP_WAIT 0x40 /* wait for peer */ +#define OBX_SRMP_NONF 0x80 /* handle GET non-final (used by server only) */ +#define OBX_SRMP_NONF_EVT 0x20 /* report GET non-final req event (used by server only) */ +#define OBX_SRMP_SESS_FST 0x01 /* mark the session resume. The SSN on first req might not match */ +typedef UINT8 tOBX_SRMP; + +/* continue to define tOBX_SESS_ST for internal use */ +enum +{ + OBX_SESS_TIMEOUT = OBX_SESS_EXT_MAX, /* 0x03 session is requested/set timeout. */ + OBX_SESS_CREATE, /* 0x04 session is requested/create. */ + OBX_SESS_SUSPEND, /* 0x05 session is requested/suspend. */ + OBX_SESS_RESUME, /* 0x06 session is requested/resume. */ + OBX_SESS_CLOSE, /* 0x07 session is requested/close. */ + OBX_SESS_SUSPENDING /* 0x08 session is requested/suspend: server has not reported the suspend event */ +}; +#define OBX_SESS_DROP (0x80|OBX_SESS_SUSPENDED) /* the session as suspended by link drop */ + +/* port control block */ +typedef struct +{ + tOBX_HANDLE handle; /* The handle of the client or session handle for server */ + UINT16 port_handle; /* Port handle of connection */ + UINT16 tx_mtu; /* The MTU of the connected peer */ + UINT16 rx_mtu; /* The MTU of this instance */ + TIMER_LIST_ENT tle; /* This session's Timer List Entry */ + tOBX_CLOSE_FN *p_close_fn; /* the close connection function */ + tOBX_SEND_FN *p_send_fn; /* the send message function */ + BT_HDR *p_txmsg; /* The message to send to peer */ + BUFFER_Q rx_q; /* received data buffer queue */ + BOOLEAN stopped; /* TRUE, if flow control the peer (stop peer from sending more data). */ + BT_HDR *p_rxmsg; /* The message received from peer */ +} tOBX_PORT_CB; + +typedef struct +{ + tOBX_PORT_CB *p_pcb; /* the port control block */ + UINT32 code; /* the event code from RFCOMM */ +} tOBX_PORT_EVT; + +/* l2cap control block */ +typedef struct +{ + tOBX_HANDLE handle; /* The handle of the client or session handle for server */ + UINT16 lcid; /* l2cap lcid of connection */ + UINT16 tx_mtu; /* The MTU of the connected peer */ + UINT16 rx_mtu; /* The MTU of this instance */ + TIMER_LIST_ENT tle; /* This session's Timer List Entry */ + tOBX_CLOSE_FN *p_close_fn; /* the close connection function */ + tOBX_SEND_FN *p_send_fn; /* the send message function */ + BT_HDR *p_txmsg; /* The message to send to peer */ + BUFFER_Q rx_q; /* received data buffer queue */ + BOOLEAN stopped; /* TRUE, if flow control the peer (stop peer from sending more data). */ + UINT8 ch_state; /* L2CAP channel state */ + UINT8 ch_flags; /* L2CAP configuration flags */ + BOOLEAN cong; /* TRUE, if L2CAP is congested. */ +} tOBX_L2C_CB; + +enum +{ + OBX_L2C_EVT_CONG, + OBX_L2C_EVT_CLOSE, + OBX_L2C_EVT_CONN_IND, + OBX_L2C_EVT_DATA_IND, + OBX_L2C_EVT_RESUME +}; +typedef UINT8 tOBX_L2C_EVT; + +typedef struct +{ + BD_ADDR bd_addr; + UINT16 lcid; + UINT16 psm; + UINT8 id; +} tOBX_L2C_IND; + +typedef union +{ + tOBX_L2C_IND conn_ind; + BOOLEAN is_cong; + BT_HDR *p_pkt; + UINT8 any; + BD_ADDR remote_addr; +} tOBX_L2C_EVT_PARAM; + +typedef struct +{ + tOBX_L2C_CB *p_l2cb; /* the L2CAP control block */ + tOBX_L2C_EVT_PARAM param; + tOBX_L2C_EVT l2c_evt; /* the event code from L2CAP */ +} tOBX_L2C_EVT_MSG; + +/* common of port & l2cap control block */ +typedef struct +{ + tOBX_HANDLE handle; /* The handle of the client or session handle for server */ + UINT16 id; /* l2cap lcid or port handle */ + UINT16 tx_mtu; /* The MTU of the connected peer */ + UINT16 rx_mtu; /* The MTU of this instance */ + TIMER_LIST_ENT tle; /* This session's Timer List Entry */ + tOBX_CLOSE_FN *p_close_fn; /* the close connection function */ + tOBX_SEND_FN *p_send_fn; /* the send message function */ + BT_HDR *p_txmsg; /* The message to send to peer */ + BUFFER_Q rx_q; /* received data buffer queue */ + BOOLEAN stopped; /* TRUE, if flow control the peer (stop peer from sending more data). */ +} tOBX_COMM_CB; + +/* lower layer control block */ +typedef union +{ + tOBX_PORT_CB port; + tOBX_L2C_CB l2c; + tOBX_COMM_CB comm; +} tOBX_LL_CB; + +/* client control block */ +typedef struct +{ + tOBX_CL_CBACK *p_cback; /* Application callback function to receive events */ + BT_HDR *p_next_req; /* This is used when the session is flow controlled by peer + * and a DISCONNECT or ABORT request is sent.*/ + BT_HDR *p_saved_req; /* Client saves a copy of the request sent to the server + * Just in case the operation is challenged by the server */ + BT_HDR *p_auth; /* The request-digest string, if challenging the server. + * The received OBEX packet, if waiting for password */ + tOBX_LL_CB ll_cb; /* lower layer control block for this client */ + UINT32 conn_id; /* Connection ID for this connection */ + UINT32 nonce; /* This is converted to UINT8[16] internally before adding to the OBEX header. This value is copied to the server control block and is increased after each use. 0, if only legacy OBEX (unreliable) session is desired. */ + BD_ADDR peer_addr; /* peer address */ + tOBX_EVT_PARAM param; /* The event parameter. */ + UINT8 sess_info[OBX_SESSION_INFO_SIZE]; /* session id + local nonce */ + tOBX_EVENT api_evt; /* Set the API event, if need to notify user outside of action function. */ + tOBX_CL_STATE state; /* The current state */ + tOBX_STATE next_state; /* Use by PART state to return to regular states */ + tOBX_CL_STATE prev_state; /* The previous state */ + UINT16 psm; /* L2CAP virtual psm */ + tOBX_SRM srm; /* Single Response Mode */ + tOBX_SRMP srmp; /* Single Response Mode Parameter */ + UINT8 ssn; /* Session Sequence number */ + tOBX_SESS_ST sess_st; /* Session state */ + UINT8 rsp_code; /* The response code of the response packet */ + BOOLEAN final; /* The final bit status of last request */ + BOOLEAN wait_auth; /* TRUE, if challenges the server and is waiting for the response */ +} tOBX_CL_CB; + +/* Authentication Control block */ +typedef struct +{ + UINT32 nonce; /* Timestamp used as nonce */ + tOBX_AUTH_OPT auth_option; /* Authentication option */ + UINT8 realm_len; /* The length of p_realm */ + UINT8 realm[1]; /* The realm of this server. Charset is the first byte */ +} tOBX_AUTH_PARAMS; + +/* suspend control block */ +typedef struct +{ + TIMER_LIST_ENT stle; /* The timer for suspended session timeout */ + BD_ADDR peer_addr; /* peer address */ + UINT8 sess_info[OBX_SESSION_INFO_SIZE]; /* session id + local nonce + conn id + state + srm */ + UINT32 offset; /* the file offset */ + tOBX_SR_STATE state; /* The current state */ + UINT8 ssn; /* the last ssn */ +} tOBX_SPND_CB; + +/* server control block */ +typedef struct +{ + tOBX_SR_CBACK *p_cback; /* Application callback function to receive events */ + tOBX_TARGET target; /* target header of this server */ + tOBX_AUTH_PARAMS *p_auth; /* A GKI buffer that holds the authentication related parameters */ + UINT8 sess[OBX_MAX_SR_SESSION]; /* index + 1 of sr_sess[]. 0, if not used. */ + tOBX_SPND_CB *p_suspend; /* the information for suspended sessions (GKI buffer) */ + UINT32 nonce; /* This is converted to UINT8[16] internally before adding to the OBEX header. This value is copied to the server control block and is increased after each use. 0, if only legacy OBEX (unreliable) session is desired. */ + UINT16 psm; /* PSM for this server */ + UINT8 max_suspend; /* the max number of tOBX_SPD_CB[] in p_suspend. must be less than OBX_MAX_SUSPEND_SESSIONS */ + UINT8 scn; /* SCN for this server */ + UINT8 num_sess; /* Max number of session control blocks used by this server*/ +} tOBX_SR_CB; + +/* server session control block */ +typedef struct +{ + BT_HDR *p_saved_msg; /* This is a message saved for authentication or GetReq-non-final */ + BT_HDR *p_next_req; /* This is a message saved for flow control reasons */ + tOBX_LL_CB ll_cb; /* lower layer control block for this session */ + UINT32 conn_id; /* Connection ID for this connection */ + BD_ADDR peer_addr; /* peer address */ + UINT8 sess_info[OBX_SESSION_INFO_SIZE]; /* session id + local nonce */ + tOBX_SR_STATE state; /* The current state */ + tOBX_SR_STATE prev_state; /* The previous state */ + tOBX_STATE next_state; /* Use by PART state to return to regular states */ + tOBX_HANDLE handle; /* The obx handle for server */ + tOBX_EVENT api_evt; /* The event to notify the API user. */ + tOBX_EVT_PARAM param; /* The event parameter. */ + tOBX_SRMP srmp; /* Single Response Mode Parameter */ + tOBX_SRM srm; /* Single Response Mode */ + tOBX_SESS_ST sess_st; /* Session state */ + UINT8 ssn; /* Next Session Sequence number */ + UINT8 cur_op; /* The op code for the current transaction (keep this for Abort reasons) */ + /* set to the current OP (non-abort) in rfc.c, set it to abort when a response is sent */ +} tOBX_SR_SESS_CB; + +typedef struct +{ +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_CB server[OBX_NUM_SERVERS];/* The server control blocks */ + UINT32 next_cid; /* Next OBEX connection ID for server */ + tOBX_SR_SESS_CB sr_sess[OBX_NUM_SR_SESSIONS]; + tOBX_L2C_CB sr_l2cb; /* for obx_l2c_connect_ind_cback */ +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) + tOBX_CL_CB client[OBX_NUM_CLIENTS];/* The client control blocks */ + tOBX_PORT_CB *p_temp_pcb; /* Valid only during client RFCOMM_CreateConnection call */ + UINT8 next_ind; /* The index to the next client control block */ +#endif + + tOBX_HANDLE hdl_map[MAX_RFC_PORTS]; /* index of this array is the port_handle, + * the value is the OBX handle */ + UINT16 l2c_map[MAX_L2CAP_CHANNELS]; /* index of this array is (lcid - L2CAP_BASE_APPL_CID) */ + UINT32 timeout_val; /* The timeout value to wait for activity from peer */ + UINT32 sess_tout_val; /* The timeout value for reliable sessions to remain in suspend */ + UINT8 trace_level; /* The default trace level */ + UINT8 num_client; /* Number of client control blocks */ + UINT8 num_server; /* Number of server control blocks */ + UINT8 num_sr_sess; /* Number of server session control blocks */ + UINT8 max_rx_qcount; /* Max Number of rx_q count */ +} tOBX_CB; + +/***************************************************************************** +** Definition for State Machine +*****************************************************************************/ + +/* Client Action functions are of this type */ +typedef tOBX_CL_STATE (*tOBX_CL_ACT)(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); + +/* Server Action functions are of this type */ +typedef tOBX_SR_STATE (*tOBX_SR_ACT)(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); + +#define OBX_SM_IGNORE 0 +#define OBX_SM_NO_ACTION 0xFF +#define OBX_SM_ALL 0x80 /* for events like close confirm, abort request, close request where event handling is the same for most of the states */ +#define OBX_SM_ENTRY_MASK 0x7F /* state machine entry mask */ + +#define OBX_SME_ACTION 0 +#define OBX_SME_NEXT_STATE 1 +#define OBX_SM_NUM_COLS 2 + +typedef const UINT8 (*tOBX_SM_TBL)[OBX_SM_NUM_COLS]; + +/***************************************************************************** +** External global data +*****************************************************************************/ +#if OBX_DYNAMIC_MEMORY == FALSE +OBX_API extern tOBX_CB obx_cb; +#else +OBX_API extern tOBX_CB *obx_cb_ptr; +#define obx_cb (*obx_cb_ptr) +#endif +extern const tOBX_EVENT obx_sm_evt_to_api_evt[]; +extern const UINT8 obx_hdr_start_offset[]; +/***************************************************************************** +** External Function Declarations +*****************************************************************************/ +/* from obx_main.c */ + +#if (defined (BT_USE_TRACES) && BT_USE_TRACES == TRUE && OBX_CLIENT_INCLUDED == TRUE) +extern const char * obx_cl_get_state_name(tOBX_CL_STATE state); +extern const char * obx_cl_get_event_name(tOBX_CL_EVENT event); +#else +#define obx_cl_get_state_name(state_num) "" +#define obx_cl_get_event_name(event_num) "" +#endif + +#if (defined (BT_USE_TRACES) && BT_USE_TRACES == TRUE && OBX_SERVER_INCLUDED == TRUE) +extern const char * obx_sr_get_state_name(tOBX_SR_STATE state); +extern const char * obx_sr_get_event_name(tOBX_SR_EVENT event); +#else +#define obx_sr_get_state_name(state_num) "" +#define obx_sr_get_event_name(event_num) "" +#endif +/* client functions in obx_main.c */ +extern void obx_cl_timeout(TIMER_LIST_ENT *p_tle); +extern tOBX_CL_CB * obx_cl_alloc_cb(void); +extern tOBX_CL_CB * obx_cl_get_cb(tOBX_HANDLE handle); +extern tOBX_CL_CB * obx_cl_get_suspended_cb(tOBX_HANDLE *p_handle, UINT8 *p_session_info); +extern void obx_cl_free_cb(tOBX_CL_CB * p_cb); + +/* server functions in obx_main.c */ +extern tOBX_SPND_CB * obx_find_suspended_session (tOBX_SR_SESS_CB *p_scb, tOBX_TRIPLET *p_triplet, UINT8 num); +extern void obx_l2c_snd_evt (tOBX_L2C_CB *p_l2cb, tOBX_L2C_EVT_PARAM param, tOBX_L2C_EVT l2c_evt); +extern void obx_sr_timeout(TIMER_LIST_ENT *p_tle); +extern void obx_sr_sess_timeout(TIMER_LIST_ENT *p_tle); +extern tOBX_HANDLE obx_sr_alloc_cb(tOBX_StartParams *p_params); +extern tOBX_SR_CB * obx_sr_get_cb(tOBX_HANDLE handle); +extern tOBX_SR_SESS_CB * obx_sr_get_scb(tOBX_HANDLE handle); +extern void obx_sr_free_cb(tOBX_HANDLE handle); +extern void obx_sr_free_scb(tOBX_SR_SESS_CB *p_scb); +extern UINT32 obx_sr_get_next_conn_id(void); +/* common functions in obx_main.c */ +extern tOBX_PORT_CB * obx_port_handle_2cb(UINT16 port_handle); +extern void obx_start_timer(tOBX_COMM_CB *p_pcb); +extern void obx_stop_timer(TIMER_LIST_ENT *p_tle); + +/* from obx_rfc.c */ +extern void obx_cl_proc_evt(tOBX_PORT_EVT *p_evt); +extern BT_HDR * obx_build_dummy_rsp(tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code); +extern void obx_sr_proc_evt(tOBX_PORT_EVT *p_evt); +extern BOOLEAN obx_rfc_snd_msg(tOBX_PORT_CB *p_pcb); +extern tOBX_STATUS obx_open_port(tOBX_PORT_CB *p_pcb, const BD_ADDR bd_addr, UINT8 scn); +extern void obx_add_port(tOBX_HANDLE obx_handle); +extern void obx_close_port(UINT16 port_handle); + +/* from obx_capi.c */ +extern tOBX_STATUS obx_prepend_req_msg(tOBX_HANDLE handle, tOBX_CL_EVENT event, UINT8 req_code, BT_HDR *p_pkt); + +/* from obx_csm.c */ +extern void obx_csm_event(tOBX_CL_CB *p_cb, tOBX_EVENT event, BT_HDR *p_msg); + +/* from obx_cact.c */ +extern void obx_ca_connect_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_snd_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_close_port(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_snd_part(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_connect_error(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_connect_fail(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_discnt_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_fail_rsp(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_state(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_start_timer(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_connect_ok(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_session_ok(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_session_cont(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_session_get(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_session_fail(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_abort(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_snd_put_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_snd_get_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_srm_snd_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_srm_put_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_srm_get_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_srm_put_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_srm_get_notify(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_save_rsp(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern tOBX_CL_STATE obx_ca_save_req(tOBX_CL_CB *p_cb, BT_HDR *p_pkt); + +/* from obx_ssm.c */ +extern void obx_ssm_event(tOBX_SR_SESS_CB *p_scb, tOBX_SR_EVENT event, BT_HDR *p_msg); + +/* from obx_sapi.c */ +extern tOBX_STATUS obx_prepend_rsp_msg(tOBX_HANDLE shandle, tOBX_SR_EVENT event, UINT8 rsp_code, BT_HDR *p_pkt); + +/* from obx_sact.c */ +extern tOBX_SR_STATE obx_sa_snd_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_snd_part(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_abort_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_op_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern BT_HDR * obx_conn_rsp(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_connect_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_wc_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_auth_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_connect_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_connection_error(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_close_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_clean_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_state(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_nc_to(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_save_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_rej_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_session_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_sess_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_wc_sess_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_session_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_put_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_get_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_srm_put_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_srm_put_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_srm_get_fcs(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_srm_get_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern tOBX_SR_STATE obx_sa_srm_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); + +/* from obx_gen.c */ +extern void obx_access_rsp_code(BT_HDR *p_pkt, UINT8 *p_rsp_code); +extern void obx_adjust_packet_len(BT_HDR *p_pkt); +extern UINT16 obx_read_header_len(UINT8 *ph); +extern BT_HDR * obx_dup_pkt(BT_HDR *p_pkt); + +/* from obx_md5 */ +extern BT_HDR * obx_unauthorize_rsp(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern void obx_session_id(UINT8 *p_sess_id, UINT8 *p_cl_addr, UINT8 * p_cl_nonce, int cl_nonce_len, + UINT8 *p_sr_addr, UINT8 * p_sr_nonce, int sr_nonce_len); + +/* from obx_l2c.c */ +extern void obx_register_l2c(tOBX_CL_CB *p_cl_cb, UINT16 psm); +extern tOBX_STATUS obx_open_l2c(tOBX_CL_CB *p_cl_cb, const BD_ADDR bd_addr); +extern tOBX_STATUS obx_l2c_sr_register (tOBX_SR_CB *p_cb); +extern void obx_close_l2c(UINT16 lcid); +extern BOOLEAN obx_l2c_snd_msg(tOBX_L2C_CB *p_l2cb); +extern void obx_sr_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg); +extern void obx_cl_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg); + +/* from obx_utils.c */ +extern UINT8 obx_verify_response(UINT8 opcode, tOBX_RX_HDR *p_rxh); +extern UINT8 obx_verify_request(UINT8 opcode, tOBX_RX_HDR *p_rxh); +extern BOOLEAN obx_sr_proc_pkt (tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt); +extern void obx_cl_proc_pkt (tOBX_CL_CB *p_cb, BT_HDR *p_pkt); +extern void obx_free_buf(tOBX_LL_CB *p_ll_cb); +extern UINT8 obx_add_timeout (tOBX_TRIPLET *p_trip, UINT32 timeout, tOBX_SESS_EVT *p_param); +extern void obx_read_timeout (tOBX_TRIPLET *p_trip, UINT8 num, UINT32 *p_timeout, UINT8 *p_toa); +#if (BT_USE_TRACES == TRUE) +extern void obxu_dump_hex (UINT8 *p, char *p_title, UINT16 len); +#else +#define obxu_dump_hex(p, p_title, len) +#endif +extern BT_HDR * obx_sr_prepend_msg(BT_HDR *p_pkt, UINT8 * p_data, UINT16 data_len); +extern BT_HDR * obx_cl_prepend_msg(tOBX_CL_CB *p_cb, BT_HDR *p_pkt, UINT8 * p_data, UINT16 data_len); +extern void obx_flow_control(tOBX_COMM_CB *p_comm); +extern UINT8 obx_read_triplet(tOBX_TRIPLET *p_trip, UINT8 num_trip, UINT8 tag); +extern UINT32 obx_read_obj_offset(tOBX_TRIPLET *p_trip, UINT8 num_trip); + +#endif /* OBX_INT_H */ diff --git a/stack/obx/obx_l2c.c b/stack/obx/obx_l2c.c new file mode 100644 index 0000000..51226d9 --- /dev/null +++ b/stack/obx/obx_l2c.c @@ -0,0 +1,1031 @@ +/***************************************************************************** +** +** Name: obx_l2c.c +** +** Description: This OBX module interfaces to L2CAP +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ + +#include <string.h> +#include "wcassert.h" +#include "data_types.h" +#include "bt_target.h" +#include "obx_api.h" +#include "obx_int.h" +#include "l2c_api.h" +#include "l2cdefs.h" +#include "port_api.h" +#include "btu.h" +#include "btm_int.h" + +/* Configuration flags. */ +#define OBX_L2C_CFG_IND_DONE 0x01 +#define OBX_L2C_CFG_CFM_DONE 0x02 +#define OBX_L2C_SECURITY_DONE 0x04 +#define OBX_L2C_CONN_RQS_DONE 0x07 + +/* "states" used for L2CAP channel */ +#define OBX_CH_IDLE 0 /* No connection */ +#define OBX_CH_CONN 1 /* Waiting for connection confirm */ +#define OBX_CH_CFG 2 /* Waiting for configuration complete */ +#define OBX_CH_OPEN 3 /* Channel opened */ + +/* callback function declarations */ +void obx_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id); +void obx_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result); +void obx_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void obx_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg); +void obx_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed); +void obx_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result); +void obx_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf); +void obx_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested); +tOBX_SR_SESS_CB * obx_lcb_2_sr_sess_cb(tOBX_L2C_CB *p_lcb); + +#if (OBX_SERVER_INCLUDED == TRUE) +/* L2CAP callback function structure for server */ +const tL2CAP_APPL_INFO obx_l2c_sr_appl = { + obx_l2c_connect_ind_cback, /* tL2CA_CONNECT_IND_CB */ + NULL, /* tL2CA_CONNECT_CFM_CB */ + NULL, /* tL2CA_CONNECT_PND_CB */ + obx_l2c_config_ind_cback, /* tL2CA_CONFIG_IND_CB */ + obx_l2c_config_cfm_cback, /* tL2CA_CONFIG_CFM_CB */ + obx_l2c_disconnect_ind_cback, /* tL2CA_DISCONNECT_IND_CB */ + obx_l2c_disconnect_cfm_cback, /* tL2CA_DISCONNECT_CFM_CB */ + NULL, /* tL2CA_QOS_VIOLATION_IND_CB */ + obx_l2c_data_ind_cback, /* tL2CA_DATA_IND_CB */ + obx_l2c_congestion_ind_cback, /* tL2CA_CONGESTION_STATUS_CB */ + NULL /* tL2CA_TX_COMPLETE_CB */ +}; +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) +/* L2CAP callback function structure for client */ +const tL2CAP_APPL_INFO obx_l2c_cl_appl = { + NULL, /* tL2CA_CONNECT_IND_CB */ + obx_l2c_connect_cfm_cback, /* tL2CA_CONNECT_CFM_CB */ + NULL, /* tL2CA_CONNECT_PND_CB */ + obx_l2c_config_ind_cback, /* tL2CA_CONFIG_IND_CB */ + obx_l2c_config_cfm_cback, /* tL2CA_CONFIG_CFM_CB */ + obx_l2c_disconnect_ind_cback, /* tL2CA_DISCONNECT_IND_CB */ + obx_l2c_disconnect_cfm_cback, /* tL2CA_DISCONNECT_CFM_CB */ + NULL, /* tL2CA_QOS_VIOLATION_IND_CB */ + obx_l2c_data_ind_cback, /* tL2CA_DATA_IND_CB */ + obx_l2c_congestion_ind_cback, /* tL2CA_CONGESTION_STATUS_CB */ + NULL /* tL2CA_TX_COMPLETE_CB */ +}; +#endif + +/* OBX eL2CAP default options */ +const tL2CAP_FCR_OPTS obx_l2c_fcr_opts_def = { + L2CAP_FCR_ERTM_MODE, /* Mandatory for Obex over L2CAP */ + OBX_FCR_OPT_TX_WINDOW_SIZE_BR_EDR, /* Tx window size over Bluetooth */ + OBX_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before disconnecting */ + OBX_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */ + OBX_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */ + L2CAP_DEFAULT_ERM_MPS /* MPS segment size */ +}; + +/******************************************************************************* +** Function obx_l2c_snd_evt +** Description Sends an L2CAP event to OBX through the BTU task. +*******************************************************************************/ +void obx_l2c_snd_evt (tOBX_L2C_CB *p_l2cb, tOBX_L2C_EVT_PARAM param, tOBX_L2C_EVT l2c_evt) +{ + BT_HDR *p_msg; + tOBX_L2C_EVT_MSG *p_evt; + UINT16 event; + + if (!p_l2cb) + return; + + p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE + sizeof(tOBX_PORT_EVT)); + WC_ASSERT(p_msg); + + if (p_l2cb->handle & OBX_CL_HANDLE_MASK) + event = BT_EVT_TO_OBX_CL_L2C_MSG; + else + event = BT_EVT_TO_OBX_SR_L2C_MSG; + + p_msg->event = event; + p_msg->len = sizeof(tOBX_L2C_EVT_MSG); + p_msg->offset = 0; + p_evt = (tOBX_L2C_EVT_MSG *)(p_msg + 1); + p_evt->l2c_evt = l2c_evt; + p_evt->p_l2cb = p_l2cb; + p_evt->param = param; + + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); +} + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_sr_scb_by_psm +** +** Description Find the server session control block for L2CAP. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_SR_SESS_CB * obx_sr_scb_by_psm (UINT16 psm) +{ + UINT32 xx, yy; + tOBX_SR_CB *p_cb; + tOBX_SR_SESS_CB *p_scb = NULL, *p_scbt; + UINT16 port_handle; + + for (xx=0; xx<obx_cb.num_server; xx++) + { + if (obx_cb.server[xx].psm == psm) + { + p_cb = &obx_cb.server[xx]; + /* find one that has not allocated a RFCOMM port */ + for (yy=0; yy<p_cb->num_sess; yy++) + { + p_scbt = &obx_cb.sr_sess[p_cb->sess[yy]-1]; + if (p_scbt->ll_cb.comm.id == 0) + { + p_scb = p_scbt; + p_scb->ll_cb.l2c.p_close_fn = obx_close_l2c; + p_scb->ll_cb.l2c.p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + break; + } + } + + if (p_scb == NULL) + { + /* check if an RFCOMM port can be freed */ + for (yy=0; yy<p_cb->num_sess; yy++) + { + p_scbt = &obx_cb.sr_sess[p_cb->sess[yy]-1]; + if (p_scbt->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg && p_scbt->state == OBX_SS_NOT_CONNECTED) + { + port_handle = p_scbt->ll_cb.port.port_handle; + p_scbt->ll_cb.port.port_handle = 0; + p_scb = p_scbt; + p_scb->ll_cb.l2c.p_close_fn = obx_close_l2c; + p_scb->ll_cb.l2c.p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + obx_close_port(port_handle); + RFCOMM_RemoveServer(port_handle); + obx_sr_free_scb(p_scbt); + break; + } + } + } + break; + } + } + + return p_scb; +} + +/******************************************************************************* +** +** Function obx_sr_proc_l2c_evt +** +** Description This is called to process BT_EVT_TO_OBX_SR_L2C_MSG +** Process server events from L2CAP. Get the associated server control +** block. If this is a request packet, stop timer. Find the +** associated API event and save it in server control block +** (api_evt). Fill the event parameter (param). +** Call obx_ssm_event() with the associated events.If the associated +** control block is not found (maybe the target header does not +** match) or busy, compose a service unavailable response and call +** obx_l2c_snd_msg(). +** Returns void +** +*******************************************************************************/ +void obx_sr_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg) +{ + tOBX_SR_SESS_CB *p_scb = NULL; + tOBX_L2C_CB *p_lcb; + BT_HDR *p_pkt=NULL; + tOBX_RX_HDR *p_rxh; + UINT8 opcode; + tOBX_L2C_IND *p_ind; + tL2CAP_CFG_INFO cfg; + UINT16 result = L2CAP_CONN_NO_RESOURCES; +#if (BT_USE_TRACES == TRUE) + UINT16 len; +#endif + tL2CAP_ERTM_INFO ertm_info; + tBT_UUID bt_uuid = {2, {UUID_PROTOCOL_OBEX}}; + tOBX_EVT_PARAM param; + tOBX_SR_CB *p_cb; + + if (p_msg == NULL || p_msg->p_l2cb == NULL) + return; + + if (p_msg->l2c_evt == OBX_L2C_EVT_CONN_IND) + { + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.mtu_present = TRUE; + + p_ind = &p_msg->param.conn_ind; + if ((p_scb = obx_sr_scb_by_psm(p_ind->psm)) != NULL) + { + memcpy(p_scb->param.conn.peer_addr, p_ind->bd_addr, BD_ADDR_LEN); + memcpy(p_scb->peer_addr, p_ind->bd_addr, BD_ADDR_LEN); + result = L2CAP_CONN_OK; + p_lcb = &p_scb->ll_cb.l2c; + cfg.mtu = p_lcb->rx_mtu; + + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + } + /* else no control channel yet, reject */ + + /* Set the FCR options: */ + ertm_info.preferred_mode = obx_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = OBX_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = OBX_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = OBX_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = OBX_FCR_TX_POOL_ID; + + /* Send L2CAP connect rsp */ + L2CA_CONNECT_RSP(p_ind->bd_addr, p_ind->id, p_ind->lcid, result, 0, &ertm_info, &bt_uuid); + + /* if result ok, proceed with connection */ + if (result == L2CAP_CONN_OK) + { + /* store LCID */ + p_lcb->lcid = p_ind->lcid; + obx_cb.l2c_map[p_ind->lcid - L2CAP_BASE_APPL_CID] = p_lcb->handle; + OBX_TRACE_DEBUG2("l2c_map[%d]=0x%x",p_ind->lcid - L2CAP_BASE_APPL_CID, p_lcb->handle ); + + /* transition to configuration state */ + p_lcb->ch_state = OBX_CH_CFG; + p_lcb->ch_flags = OBX_L2C_SECURITY_DONE; + + /* Send L2CAP config req */ + L2CA_CONFIG_REQ(p_ind->lcid, &cfg); + } + return; + } + + p_lcb = p_msg->p_l2cb; + p_scb = obx_lcb_2_sr_sess_cb(p_lcb); + if (p_scb == NULL) + return; + + switch (p_msg->l2c_evt) + { + case OBX_L2C_EVT_RESUME: + p_cb = &obx_cb.server[p_scb->handle - 1]; + param.ssn = p_scb->ssn; + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, param, p_pkt); + break; + + case OBX_L2C_EVT_CONG: + p_lcb->cong = p_msg->param.is_cong; + obx_ssm_event (p_scb, OBX_FCS_SET_SEVT, NULL); + break; + + case OBX_L2C_EVT_CLOSE: + obx_ssm_event (p_scb, OBX_PORT_CLOSE_SEVT, NULL); + break; + + case OBX_L2C_EVT_DATA_IND: + p_pkt = p_msg->param.p_pkt; + OBX_TRACE_DEBUG2("obx_sr_proc_l2c_evt len:%d, offset:%d", p_pkt->len, p_pkt->offset ); +#if (BT_USE_TRACES == TRUE) + len = p_pkt->len; + if (len > 0x20) + len = 0x20; + obxu_dump_hex ((UINT8 *)(p_pkt + 1) + p_pkt->offset, "rsp evt", len); +#endif + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + opcode = *((UINT8 *)(p_pkt + 1) + p_pkt->offset); + memset(p_rxh, 0, sizeof(tOBX_RX_HDR)); + if (obx_verify_request (opcode, p_rxh) == OBX_BAD_SM_EVT) + { + OBX_TRACE_ERROR1("bad opcode:0x%x disconnect now", opcode ); + GKI_freebuf(p_pkt); + /* coverity [overrun-call] */ + obx_ssm_event(p_scb, OBX_TX_EMPTY_SEVT, NULL); + return; + } + p_pkt->event = obx_sm_evt_to_api_evt[p_rxh->sm_evt]; + p_pkt->layer_specific = GKI_get_buf_size(p_pkt) - BT_HDR_SIZE - p_pkt->offset - p_pkt->len; + OBX_TRACE_DEBUG3("opcode:0x%x event:%d sm_evt:%d", opcode, p_pkt->event, p_rxh->sm_evt ); + if (p_pkt->event != OBX_BAD_SM_EVT) + { + if (GKI_queue_is_empty(&p_lcb->rx_q) && (p_scb->srm & OBX_SRM_WAIT_UL) == 0) + { + obx_sr_proc_pkt (p_scb, p_pkt); + } + else + { + GKI_enqueue (&p_lcb->rx_q, p_pkt); + if (p_lcb->rx_q.count > obx_cb.max_rx_qcount) + { + p_lcb->stopped = TRUE; + L2CA_FlowControl(p_lcb->lcid, FALSE); + } + OBX_TRACE_DEBUG4 ("obx_sr_proc_l2c_evt stopped:%d state:%d rx_q.count:%d, srm:0x%x", + p_lcb->stopped, p_scb->state, p_lcb->rx_q.count, p_scb->srm ); + } + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + break; + } +} + +/******************************************************************************* +** +** Function obx_l2c_sr_register +** +** Description register the PSM to L2CAP. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_STATUS obx_l2c_sr_register (tOBX_SR_CB *p_cb) +{ + tOBX_STATUS status = OBX_NO_RESOURCES; + + if (L2CA_REGISTER (p_cb->psm, &obx_l2c_sr_appl, AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE)) + { + status = OBX_SUCCESS; + } + return status; +} +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function obx_lcb_2_sr_sess_cb +** +** Description Find the client control block for the given l2cap session. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_SR_SESS_CB * obx_lcb_2_sr_sess_cb(tOBX_L2C_CB *p_lcb) +{ + UINT32 xx, yy; + tOBX_SR_CB *p_cb; + tOBX_SR_SESS_CB *p_scb = NULL; + + for (xx=0; xx<obx_cb.num_server; xx++) + { + if (obx_cb.server[xx].num_sess) + { + p_cb = &obx_cb.server[xx]; + for (yy=0; yy<p_cb->num_sess; yy++) + { + if (&(obx_cb.sr_sess[p_cb->sess[yy]-1].ll_cb.l2c) == p_lcb) + { + p_scb = &(obx_cb.sr_sess[p_cb->sess[yy]-1]); + break; + } + } + + if (p_scb) + break; + } + } + return p_scb; +} +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_lcb_2_clcb +** +** Description Find the client control block for the given l2cap session. +** +** +** Returns void +** +*******************************************************************************/ +tOBX_CL_CB * obx_lcb_2_clcb(tOBX_L2C_CB *p_lcb) +{ + UINT32 xx; + tOBX_CL_CB *p_cl_cb = NULL; + + for (xx=0; xx<obx_cb.num_client; xx++) + { + if (&obx_cb.client[xx].ll_cb.l2c == p_lcb) + { + p_cl_cb = &obx_cb.client[xx]; + break; + } + } + return p_cl_cb; +} +#endif + +/******************************************************************************* +** +** Function obx_lcid_2lcb +** +** Description Given a lcid, return the associated client or server +** control block. +** +** Returns +** +*******************************************************************************/ +tOBX_L2C_CB * obx_lcid_2lcb(UINT16 lcid) +{ + tOBX_L2C_CB *p_lcb = NULL; + tOBX_HANDLE obx_handle = 0; +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_CB *p_cb; +#endif + tOBX_HANDLE obx_mskd_handle; + + /* this function is called by obx_rfc_cback() only. + * assume that port_handle is within range */ + obx_handle = obx_cb.l2c_map[lcid-L2CAP_BASE_APPL_CID]; + obx_mskd_handle = obx_handle&OBX_HANDLE_MASK; + OBX_TRACE_DEBUG3("obx_lcid_2lcb lcid:0x%x obx_handle:0x%x obx_mskd_handle:0x%x", + lcid, obx_handle, obx_mskd_handle); + + if (obx_handle > 0) + { + if (obx_mskd_handle & OBX_CL_HANDLE_MASK) + { +#if (OBX_CLIENT_INCLUDED == TRUE) + obx_mskd_handle &= ~OBX_CL_HANDLE_MASK; + p_lcb = &obx_cb.client[obx_mskd_handle - 1].ll_cb.l2c; +#endif /* OBX_CLIENT_INCLUDED */ + } + else if (obx_mskd_handle < OBX_NUM_SERVERS) + { +#if (OBX_SERVER_INCLUDED == TRUE) + p_cb = &obx_cb.server[obx_mskd_handle - 1]; + p_lcb = &obx_cb.sr_sess[p_cb->sess[OBX_DEC_SESS_IND(obx_handle)]-1].ll_cb.l2c; + OBX_TRACE_DEBUG3("p_lcb lcid:0x%x sess_ind:%d, sr_sess[%d]", + p_lcb->lcid, OBX_DEC_SESS_IND(obx_handle), p_cb->sess[OBX_DEC_SESS_IND(obx_handle)]-1); +#endif /* OBX_SERVER_INCLUDED */ + } + } + + return p_lcb; +} + +/******************************************************************************* +** +** Function obx_l2c_checks_ch_flags +** +** Description This function processes the L2CAP configuration indication +** event. +** +** Returns void +** +*******************************************************************************/ +static void obx_l2c_checks_ch_flags (tOBX_L2C_CB *p_lcb) +{ + tOBX_L2C_EVT_PARAM evt_param; + + OBX_TRACE_DEBUG1 ("obx_l2c_checks_ch_flags ch_flags:0x%x ", p_lcb->ch_flags); + /* if all the required ch_flags are set, report the OPEN event now */ + if ((p_lcb->ch_flags & OBX_L2C_CONN_RQS_DONE) == OBX_L2C_CONN_RQS_DONE) + { + p_lcb->ch_state = OBX_CH_OPEN; + obx_start_timer((tOBX_COMM_CB *)p_lcb); + evt_param.is_cong = FALSE; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CONG); + } +} + +/******************************************************************************* +** +** Function obx_l2c_connect_ind_cback +** +** Description This is the L2CAP connect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +#if (OBX_SERVER_INCLUDED == TRUE) +void obx_l2c_connect_ind_cback(BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id) +{ + tOBX_L2C_EVT_PARAM evt_param; + + obx_cb.sr_l2cb.handle = 0; /* to mark as server event */ + memcpy( evt_param.conn_ind.bd_addr, bd_addr, BD_ADDR_LEN); + evt_param.conn_ind.lcid = lcid; + evt_param.conn_ind.psm = psm; + evt_param.conn_ind.id = id; + obx_l2c_snd_evt (&obx_cb.sr_l2cb, evt_param, OBX_L2C_EVT_CONN_IND); +} +#endif /* OBX_SERVER_INCLUDED */ + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_cl_proc_l2c_evt +** +** Description This is called to process BT_EVT_TO_OBX_CL_L2C_MSG +** Process client events from L2CAP. Get the associated client control +** block. If this is a response packet, stop timer. Call +** obx_csm_event() with event OK_CFM, FAIL_CFM or CONT_CFM. +** Returns void +** +*******************************************************************************/ +void obx_cl_proc_l2c_evt (tOBX_L2C_EVT_MSG *p_msg) +{ + tOBX_CL_CB *p_cl_cb = NULL; + tOBX_L2C_CB *p_l2cb; + BT_HDR *p_pkt; + tOBX_RX_HDR *p_rxh; + UINT8 opcode; + + if (p_msg == NULL || p_msg->p_l2cb == NULL) + return; + + p_l2cb = p_msg->p_l2cb; + p_cl_cb = obx_lcb_2_clcb(p_l2cb); + if (p_cl_cb == NULL) + return; + + switch (p_msg->l2c_evt) + { + case OBX_L2C_EVT_CONG: + p_msg->p_l2cb->cong = p_msg->param.is_cong; + obx_csm_event (p_cl_cb, OBX_FCS_SET_CEVT, NULL); + break; + + case OBX_L2C_EVT_CLOSE: + obx_csm_event (p_cl_cb, OBX_PORT_CLOSE_CEVT, NULL); + break; + + case OBX_L2C_EVT_DATA_IND: + p_pkt = p_msg->param.p_pkt; + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + opcode = *((UINT8 *)(p_pkt + 1) + p_pkt->offset); + memset(p_rxh, 0, sizeof(tOBX_RX_HDR)); + obx_verify_response (opcode, p_rxh); + + OBX_TRACE_DEBUG4 ("obx_cl_proc_l2c_evt event:0x%x/0x%x state:%d srm:0x%x", p_pkt->event, p_rxh->sm_evt, p_cl_cb->state, p_cl_cb->srm ); + if (p_rxh->sm_evt != OBX_BAD_SM_EVT) + { + if (GKI_queue_is_empty(&p_l2cb->rx_q) && (p_cl_cb->srm & OBX_SRM_WAIT_UL) == 0) + { + obx_cl_proc_pkt (p_cl_cb, p_pkt); + } + else + { + GKI_enqueue (&p_l2cb->rx_q, p_pkt); + if (p_l2cb->rx_q.count > obx_cb.max_rx_qcount) + { + p_l2cb->stopped = TRUE; + L2CA_FlowControl(p_l2cb->lcid, FALSE); + } + OBX_TRACE_DEBUG3 ("obx_cl_proc_l2c_evt rx_q.count:%d, stopped:%d state:%d", p_l2cb->rx_q.count, p_l2cb->stopped, p_cl_cb->state ); + } + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + break; + + } +} + +/******************************************************************************* +** +** Function obx_l2c_sec_check_complete +** +** Description The function called when Security Manager finishes +** verification of the service side connection +** +** Returns void +** +*******************************************************************************/ +static void obx_l2c_sec_check_complete (BD_ADDR bd_addr, void *p_ref_data, UINT8 res) +{ + tOBX_L2C_CB *p_lcb = (tOBX_L2C_CB *)p_ref_data; + + OBX_TRACE_DEBUG3 ("obx_l2c_sec_check_complete ch_state:%d, ch_flags:0x%x, status:%d", + p_lcb->ch_state, p_lcb->ch_flags, res); + if (p_lcb->ch_state == OBX_CH_IDLE) + return; + + if (res == BTM_SUCCESS) + { + p_lcb->ch_flags |= OBX_L2C_SECURITY_DONE; + obx_l2c_checks_ch_flags (p_lcb); + } + else + { + /* security failed - disconnect the channel */ + L2CA_DISCONNECT_REQ (p_lcb->lcid); + } +} + +/******************************************************************************* +** +** Function obx_l2c_connect_cfm_cback +** +** Description This is the L2CAP connect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_connect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tOBX_L2C_CB *p_lcb; + tL2CAP_CFG_INFO cfg; + tOBX_L2C_EVT_PARAM evt_param; + tOBX_CL_CB *p_cl_cb = NULL; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + OBX_TRACE_DEBUG1("obx_l2c_connect_cfm_cback ch_state:%d", p_lcb->ch_state); + /* if in correct state */ + if (p_lcb->ch_state == OBX_CH_CONN) + { + /* if result successful */ + if (result == L2CAP_CONN_OK) + { + /* set channel state */ + p_lcb->ch_state = OBX_CH_CFG; + + p_cl_cb = obx_lcb_2_clcb(p_lcb); + btm_sec_mx_access_request (p_cl_cb->peer_addr, p_cl_cb->psm, TRUE, + 0, 0, &obx_l2c_sec_check_complete, p_lcb); + + /* Send L2CAP config req */ + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + + cfg.mtu_present = TRUE; + cfg.mtu = p_lcb->rx_mtu; + + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + + L2CA_CONFIG_REQ(lcid, &cfg); + } + /* else failure */ + else + { + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } + } + } +} +#endif /* OBX_CLIENT_INCLUDED */ + + +/******************************************************************************* +** +** Function obx_l2c_config_cfm_cback +** +** Description This is the L2CAP config confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_config_cfm_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tOBX_L2C_CB *p_lcb; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + /* if in correct state */ + if (p_lcb->ch_state == OBX_CH_CFG) + { + /* if result successful */ + if (p_cfg->result == L2CAP_CFG_OK) + { + /* update flags */ + p_lcb->ch_flags |= OBX_L2C_CFG_CFM_DONE; + + /* if configuration complete */ + obx_l2c_checks_ch_flags(p_lcb); + } + /* else failure */ + else + { + /* store result value + p_lcb->ch_result = p_cfg->result; */ + + /* Send L2CAP disconnect req */ + L2CA_DISCONNECT_REQ(lcid); + } + } + } +} + +/******************************************************************************* +** +** Function obx_l2c_config_ind_cback +** +** Description This is the L2CAP config indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_config_ind_cback(UINT16 lcid, tL2CAP_CFG_INFO *p_cfg) +{ + tOBX_L2C_CB *p_lcb; + UINT16 max_mtu = OBX_MAX_MTU; + + /* Don't include QoS nor flush timeout in the response since we + currently always accept these values. Note: fcr_present is left + untouched since l2cap negotiates this internally + */ + p_cfg->flush_to_present = FALSE; + p_cfg->qos_present = FALSE; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + /* store the mtu in tbl */ + if (p_cfg->mtu_present) + { + p_lcb->tx_mtu = p_cfg->mtu; + } + else + { + p_lcb->tx_mtu = L2CAP_DEFAULT_MTU; + } + + if (p_lcb->tx_mtu > max_mtu) + { + p_lcb->tx_mtu = p_cfg->mtu = max_mtu; + + /* Must tell the peer what the adjusted value is */ + p_cfg->mtu_present = TRUE; + } + else /* Don't include in the response */ + p_cfg->mtu_present = FALSE; + OBX_TRACE_DEBUG2 ("obx_l2c_config_ind_cback tx_mtu:%d use:%d", p_lcb->tx_mtu, max_mtu); + + p_cfg->result = L2CAP_CFG_OK; + + /* send L2CAP configure response */ + L2CA_CONFIG_RSP(lcid, p_cfg); + + if (p_cfg->result != L2CAP_CFG_OK) + { + return; + } + + /* if first config ind */ + if ((p_lcb->ch_flags & OBX_L2C_CFG_IND_DONE) == 0) + { + /* update flags */ + p_lcb->ch_flags |= OBX_L2C_CFG_IND_DONE; + + /* if configuration complete */ + obx_l2c_checks_ch_flags(p_lcb); + } + } +} + +/******************************************************************************* +** +** Function obx_l2c_disconnect_ind_cback +** +** Description This is the L2CAP disconnect indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_disconnect_ind_cback(UINT16 lcid, BOOLEAN ack_needed) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + if (ack_needed) + { + /* send L2CAP disconnect response */ + L2CA_DISCONNECT_RSP(lcid); + } + + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } +} + +/******************************************************************************* +** +** Function obx_l2c_disconnect_cfm_cback +** +** Description This is the L2CAP disconnect confirm callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_disconnect_cfm_cback(UINT16 lcid, UINT16 result) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CLOSE); + } +} + +/******************************************************************************* +** +** Function obx_l2c_data_ind_cback +** +** Description This is the L2CAP data indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_data_ind_cback(UINT16 lcid, BT_HDR *p_buf) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; +#if (BT_USE_TRACES == TRUE) + UINT16 len; +#endif + + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.p_pkt = p_buf; + OBX_TRACE_DEBUG3("obx_l2c_data_ind_cback 0x%x, len:%d, offset:%d", p_buf, p_buf->len, p_buf->offset ); +#if (BT_USE_TRACES == TRUE) + len = p_buf->len; + if (len > 0x20) + len = 0x20; + obxu_dump_hex ((UINT8 *)(p_buf + 1) + p_buf->offset, "rsp cback", len); +#endif + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_DATA_IND); + } + else /* prevent buffer leak */ + GKI_freebuf(p_buf); +} + + +/******************************************************************************* +** +** Function obx_l2c_congestion_ind_cback +** +** Description This is the L2CAP congestion indication callback function. +** +** +** Returns void +** +*******************************************************************************/ +void obx_l2c_congestion_ind_cback(UINT16 lcid, BOOLEAN is_congested) +{ + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + OBX_TRACE_DEBUG2("obx_l2c_congestion_ind_cback lcid:%d, is_congested:%d",lcid, is_congested ); + /* look up lcb for this channel */ + if ((p_lcb = obx_lcid_2lcb(lcid)) != NULL) + { + evt_param.is_cong = is_congested; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_CONG); + } +} + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_register_l2c +** Description Call L2CA_Register() to get virtual psm. +** Returns +*******************************************************************************/ +void obx_register_l2c(tOBX_CL_CB *p_cl_cb, UINT16 psm) +{ + p_cl_cb->psm = L2CA_REGISTER (psm, &obx_l2c_cl_appl, AMP_AUTOSWITCH_ALLOWED|AMP_USE_AMP_IF_POSSIBLE); +} + +/******************************************************************************* +** Function obx_open_l2c +** Description Call L2CA_Register() & L2CA_ConnectReq() to get lcid. +** Returns port handle +*******************************************************************************/ +tOBX_STATUS obx_open_l2c(tOBX_CL_CB *p_cl_cb, const BD_ADDR bd_addr) +{ + tOBX_L2C_CB *p_l2cb = &p_cl_cb->ll_cb.l2c; + tOBX_STATUS status = OBX_NO_RESOURCES; /* successful */ + UINT16 max_mtu = OBX_MAX_MTU; + tL2CAP_CFG_INFO cfg; + tL2CAP_ERTM_INFO ertm_info; + tBT_UUID bt_uuid = {2, {UUID_PROTOCOL_OBEX}}; + + OBX_TRACE_DEBUG2("obx_open_l2c rxmtu:%d, cbmtu:%d", p_l2cb->rx_mtu, max_mtu ); + + /* clear buffers from previous connection */ + obx_free_buf(&p_cl_cb->ll_cb); + + /* make sure the MTU is in registered range */ + if (p_l2cb->rx_mtu > max_mtu) + p_l2cb->rx_mtu = max_mtu; + if (p_l2cb->rx_mtu < OBX_MIN_MTU) + p_l2cb->rx_mtu = OBX_MIN_MTU; + + if (p_cl_cb->psm) + { + memcpy (p_cl_cb->peer_addr, bd_addr, BD_ADDR_LEN); + + /* Set the FCR options: */ + ertm_info.preferred_mode = obx_l2c_fcr_opts_def.mode; + ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM; + ertm_info.user_rx_pool_id = OBX_USER_RX_POOL_ID; + ertm_info.user_tx_pool_id = OBX_USER_TX_POOL_ID; + ertm_info.fcr_rx_pool_id = OBX_FCR_RX_POOL_ID; + ertm_info.fcr_tx_pool_id = OBX_FCR_TX_POOL_ID; + + p_l2cb->lcid = L2CA_CONNECT_REQ (p_cl_cb->psm, (BD_ADDR_PTR)bd_addr, &ertm_info, &bt_uuid); + if (p_l2cb->lcid) + { + p_l2cb->ch_state = OBX_CH_CONN; + p_l2cb->ch_flags = 0; + p_l2cb->cong = TRUE; + memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO)); + cfg.fcr_present = TRUE; + cfg.fcr = obx_l2c_fcr_opts_def; + status = OBX_SUCCESS; + } + } + + OBX_TRACE_DEBUG3("obx_open_l2c rxmtu:%d, lcid:%d, l2c.handle:0x%x", + p_l2cb->rx_mtu, p_l2cb->lcid, p_l2cb->handle ); + + if (status == OBX_SUCCESS) + { + obx_cb.l2c_map[p_l2cb->lcid - L2CAP_BASE_APPL_CID] = p_l2cb->handle; + p_l2cb->p_send_fn = (tOBX_SEND_FN *)obx_l2c_snd_msg; + p_l2cb->p_close_fn = obx_close_l2c; + } + else + { + status = OBX_NO_RESOURCES; + } + + return status; +} +#endif + +/******************************************************************************* +** +** Function obx_close_l2c +** Description Clear the port event mask and callback. Close the port. +** Returns void +*******************************************************************************/ +void obx_close_l2c(UINT16 lcid) +{ + L2CA_DISCONNECT_REQ (lcid); +} + +/******************************************************************************* +** Function obx_l2c_snd_msg +** Description Call PORT_WriteData() to send an OBEX message to peer. If +** all data is sent, free the GKI buffer that holds +** the OBEX message. If only portion of data is +** sent, adjust the BT_HDR for PART state. +** Returns TRUE if all data is sent +*******************************************************************************/ +BOOLEAN obx_l2c_snd_msg(tOBX_L2C_CB *p_l2cb) +{ + BOOLEAN sent = FALSE; + + if (!p_l2cb->cong) + { + OBX_TRACE_DEBUG2("obx_l2c_snd_msg len:%d, offset:0x%x", p_l2cb->p_txmsg->len, p_l2cb->p_txmsg->offset); + + obx_stop_timer(&p_l2cb->tle); + if (L2CA_DATA_WRITE (p_l2cb->lcid, p_l2cb->p_txmsg) == L2CAP_DW_CONGESTED) + { + OBX_TRACE_DEBUG0("obx_l2c_snd_msg congested"); + p_l2cb->cong = TRUE; + } + obx_start_timer ((tOBX_COMM_CB *)p_l2cb); + p_l2cb->p_txmsg = NULL; + sent = TRUE; + } + + return sent; +} diff --git a/stack/obx/obx_main.c b/stack/obx/obx_main.c new file mode 100644 index 0000000..7dc7c97 --- /dev/null +++ b/stack/obx/obx_main.c @@ -0,0 +1,946 @@ +/***************************************************************************** +** +** Name: obx_main.c +** +** File: OBEX common API and interface to other Bluetooth modules +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "bt_target.h" +#include "obx_int.h" +#include "btu.h" /* for timer */ +#include "l2c_api.h" +#include "l2cdefs.h" + +#if (defined (BT_USE_TRACES) && BT_USE_TRACES == TRUE) +const char * const obx_cl_state_name [] = +{ + "NULL", + "NOT_CONN", + "SESS_RS", + "CONN_RS", + "UNAUTH", + "CONN", + "DISCNT_RS", + "OP_UNAUTH", + "SETPATH_RS", + "ACT_RS", + "ABORT_RS", + "PUT_RS", + "GET_RS", + "PUT", + "GET", + "PUT_S", + "GET_S", + "PART", + "Unknown" +}; + +const char * const obx_cl_event_name [] = +{ + "CONN_R", + "SESS_R", + "DISCNT_R", + "PUT_R", + "GET_R", + "SETPATH_R", + "ACT_R", + "ABORT_R", + "OK_C", + "CONT_C", + "FAIL_C", + "PORT_CLS", + "TX_EMPTY", + "FCS_SET", + "STATE", + "TIMEOUT", + "Unknown" +}; + +const char * const obx_sr_state_name [] = +{ + "NULL", + "NOT_CONN", + "SESS_I", + "CONN_I", + "WAIT_AUTH", + "AUTH_I", + "CONN", + "DISCNT_I", + "SETPATH_I", + "ACT_I", + "ABORT_I", + "PUT_I", + "GET_I", + "PUT", + "GET", + "PUT_S", + "GET_S", + "PART", + "WAIT_CLS", + "Unknown" +}; + +const char * const obx_sr_event_name [] = +{ + "CONN_R", + "SESS_R", + "DISCNT_R", + "PUT_R", + "GET_R", + "SETPATH_R", + "ACT_R", + "ABORT_R", + "CONN_C", + "SESS_C", + "DISCNT_C", + "PUT_C", + "GET_C", + "SETPATH_C", + "ACT_C", + "ABORT_C", + "PORT_CLS", + "FCS_SET", + "STATE", + "TIMEOUT", + "BAD_REQ", + "TX_EMPTY", + "Unknown" +}; +#endif + +#if OBX_DYNAMIC_MEMORY == FALSE +OBX_API tOBX_CB obx_cb; +#endif + + +#if (defined (BT_USE_TRACES) && BT_USE_TRACES == TRUE) +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_cl_get_state_name +** Returns The client state name. +*******************************************************************************/ +const char *obx_cl_get_state_name(tOBX_CL_STATE state) +{ + const char * p_str = obx_cl_state_name[OBX_CS_MAX]; + if (state < OBX_CS_MAX) + { + p_str = obx_cl_state_name[state]; + } + return p_str; +} + +/******************************************************************************* +** Function obx_cl_get_event_name +** Returns The client event name. +*******************************************************************************/ +const char *obx_cl_get_event_name(tOBX_CL_EVENT event) +{ + const char * p_str = obx_cl_event_name[OBX_MAX_CEVT]; + if (event < OBX_MAX_CEVT) + { + p_str = obx_cl_event_name[event]; + } + return p_str; +} +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_sr_get_state_name +** Returns The server state name. +*******************************************************************************/ +const char *obx_sr_get_state_name(tOBX_SR_STATE state) +{ + const char * p_str = obx_sr_state_name[OBX_SS_MAX]; + if (state < OBX_SS_MAX) + { + p_str = obx_sr_state_name[state]; + } + return p_str; +} + +/******************************************************************************* +** Function obx_sr_get_event_name +** Returns The server event name. +*******************************************************************************/ +const char *obx_sr_get_event_name(tOBX_SR_EVENT event) +{ + const char * p_str = obx_sr_event_name[OBX_MAX_SEVT]; + if (event < OBX_MAX_SEVT) + { + p_str = obx_sr_event_name[event]; + } + return p_str; +} +#endif +#endif + +/******************************************************************************* +** Function obx_start_timer +** Description start BTU timer. +*******************************************************************************/ +void obx_start_timer(tOBX_COMM_CB *p_pcb) +{ + UINT16 type = 0; + UINT32 timeout = obx_cb.timeout_val; + + if (timeout) + { + if (p_pcb->handle & OBX_CL_HANDLE_MASK) + { + if (p_pcb->p_txmsg && p_pcb->p_txmsg->event == OBX_DISCONNECT_REQ_EVT) + { + timeout = OBX_DISC_TOUT_VALUE; + } + type = BTU_TTYPE_OBX_CLIENT_TO; + } + else + { + if (p_pcb->p_txmsg && p_pcb->p_txmsg->event == (OBX_DISCNT_CFM_SEVT + 1)) + { + timeout = OBX_DISC_TOUT_VALUE; + } + type = BTU_TTYPE_OBX_SERVER_TO; + } + btu_start_timer(&p_pcb->tle, type, timeout); + } + OBX_TRACE_DEBUG3("obx_start_timer type: %d, val:%d, p_tle:0x%x", type, timeout, &p_pcb->tle); +} + +/******************************************************************************* +** Function obx_stop_timer +** Description Stop BTU timer +*******************************************************************************/ +void obx_stop_timer(TIMER_LIST_ENT *p_tle) +{ + btu_stop_timer(p_tle); + OBX_TRACE_DEBUG1("obx_stop_timer p_tle:0x%x", p_tle); +} + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_cl_timeout +** Description Get client control block from timer param. Start BTU timer again. +** Call application callback routine with OBX_TIMEOUT_EVT event. +*******************************************************************************/ +void obx_cl_timeout(TIMER_LIST_ENT *p_tle) +{ + tOBX_CL_CB *p_cb = (tOBX_CL_CB *) p_tle->param; + tOBX_EVT_PARAM evtp; + tOBX_HANDLE handle = p_cb->ll_cb.comm.handle; + tOBX_CL_CBACK *p_cback = p_cb->p_cback; + + memset(&evtp, 0, sizeof(tOBX_EVT_PARAM)); + if (obx_cb.timeout_val) + btu_start_timer(p_tle, BTU_TTYPE_OBX_CLIENT_TO, obx_cb.timeout_val); + obx_csm_event(p_cb, OBX_TIMEOUT_CEVT, NULL); + (*p_cback) (handle, OBX_TIMEOUT_EVT, OBX_RSP_DEFAULT, evtp, NULL); +} + +/******************************************************************************* +** +** Function obx_cl_alloc_cb +** +** Description allocate a client control block. +** +*******************************************************************************/ +tOBX_CL_CB *obx_cl_alloc_cb(void) +{ + int xx, yy; + tOBX_CL_CB *p_cb = NULL; + + /* allocate a client control block */ + for (xx=0, yy=obx_cb.next_ind; xx<obx_cb.num_client; xx++, yy++) + { + if (yy >= obx_cb.num_client) + yy = 0; + p_cb = &obx_cb.client[yy]; + + if (p_cb->ll_cb.comm.handle == OBX_HANDLE_NULL) + { + p_cb->ll_cb.comm.handle = OBX_CL_HANDLE_MASK | (yy + 1); + obx_cb.next_ind = yy+1; /* it will be adjusted, so we do not need to check the range now */ + OBX_TRACE_DEBUG3("obx_cl_alloc_cb obx handle:0x%x, yy:%d, next: %d", + p_cb->ll_cb.comm.handle, yy, obx_cb.next_ind); + p_cb->ll_cb.comm.tx_mtu = 0; + p_cb->conn_id = OBX_INVALID_CONN_ID; + + p_cb->ll_cb.comm.tle.param = (UINT32)p_cb; + p_cb->psm = 0; + p_cb->srm = 0; + break; + } + } + + if (xx == obx_cb.num_client) + p_cb = NULL; + return p_cb; +} + +/******************************************************************************* +** +** Function obx_cl_get_cb +** +** Description Returns the pointer to the client control block with given handle. +** +*******************************************************************************/ +tOBX_CL_CB *obx_cl_get_cb(tOBX_HANDLE handle) +{ + tOBX_CL_CB *p_cb = NULL; + UINT8 ind = (handle & OBX_CL_CB_IND_MASK); + + if (handle & OBX_CL_HANDLE_MASK) + { + if (ind <= obx_cb.num_client && ind > 0) + { + if (obx_cb.client[--ind].ll_cb.comm.handle == handle) + p_cb = &obx_cb.client[ind]; + } + } + + return p_cb; +} + +/******************************************************************************* +** +** Function obx_cl_get_suspended_cb +** +** Description Returns the pointer to the client control block with given handle. +** +*******************************************************************************/ +tOBX_CL_CB *obx_cl_get_suspended_cb(tOBX_HANDLE *p_handle, UINT8 *p_session_info) +{ + tOBX_HANDLE handle = *p_handle; + tOBX_CL_CB *p_cb = NULL; + tOBX_CL_CB *p_ccb = NULL; + UINT8 ind = (handle & OBX_CL_CB_IND_MASK); + + OBX_TRACE_DEBUG1("obx_cl_get_suspended_cb handle: 0x%x", handle); + if (handle & OBX_CL_HANDLE_MASK) + { + if (ind <= obx_cb.num_client && ind > 0) + { + if (obx_cb.client[--ind].ll_cb.comm.handle == handle) + { + p_ccb = &obx_cb.client[ind]; + if (p_ccb->sess_st == OBX_SESS_SUSPENDED && + ((p_session_info == p_ccb->sess_info) || (memcmp(p_session_info, p_ccb->sess_info, OBX_SESSION_INFO_SIZE) == 0))) + { + OBX_TRACE_DEBUG0("found a suspended session"); + p_cb = p_ccb; + } + } + } + } + if (p_cb == NULL) /* if the handle is NULL, assume the system was power cycled. attempt to allocate a control block */ + { + p_cb = obx_cl_alloc_cb(); + if (p_cb) + { + *p_handle = p_cb->ll_cb.comm.handle; + p_cb->sess_st = OBX_SESS_SUSPENDED; + memcpy(p_cb->sess_info, p_session_info, OBX_SESSION_INFO_SIZE); + OBX_TRACE_DEBUG1("allocated a suspended session handle: 0x%x", *p_handle); + } + } + + return p_cb; +} + +/******************************************************************************* +** +** Function obx_cl_free_cb +** +** Description Free the given client control block. +** +** Returns void +** +*******************************************************************************/ +void obx_cl_free_cb(tOBX_CL_CB * p_cb) +{ + if (p_cb) + { + OBX_TRACE_DEBUG2("obx_cl_free_cb id: %d, sess_st:%d", p_cb->ll_cb.comm.id, p_cb->sess_st); + + if (p_cb->ll_cb.comm.id>0) + { + if (p_cb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + obx_cb.hdl_map[p_cb->ll_cb.port.port_handle - 1] = 0; + else + obx_cb.l2c_map[p_cb->ll_cb.l2c.lcid - L2CAP_BASE_APPL_CID] = 0; + } + + /* make sure the GKI buffers are freed */ + if (p_cb->p_next_req) + GKI_freebuf(p_cb->p_next_req); + + if (p_cb->p_saved_req) + GKI_freebuf(p_cb->p_saved_req); + + if (p_cb->p_auth) + GKI_freebuf(p_cb->p_auth); + + obx_free_buf (&p_cb->ll_cb); + + if (p_cb->psm) + L2CA_DEREGISTER (p_cb->psm); + + /* make sure the timer is stopped */ + btu_stop_timer(&p_cb->ll_cb.comm.tle); + + memset(p_cb, 0, sizeof(tOBX_CL_CB) ); + } +} +#endif /* OBX_CLIENT_INCLUDED */ + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_find_suspended_session +** Description if p_triplet is NULL, +** check if there's still room for a reliable session +** else check if the given session is still in the suspended list +*******************************************************************************/ +tOBX_SPND_CB *obx_find_suspended_session (tOBX_SR_SESS_CB *p_scb, tOBX_TRIPLET *p_triplet, UINT8 num) +{ + UINT8 ind; + BOOLEAN found = FALSE; + BOOLEAN infinite = FALSE; + UINT8 *p, xx; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + tOBX_SPND_CB *p_spndcb, *p_ret = NULL; + + OBX_TRACE_DEBUG0("obx_find_suspended_session "); + if (p_triplet == NULL) + { + if (p_cb->p_suspend) + { + for (xx=0, p_spndcb=p_cb->p_suspend; xx<p_cb->max_suspend; xx++, p_spndcb++) + { + if ((p_spndcb->state == OBX_SS_NULL) || (memcmp(p_spndcb->peer_addr, p_scb->peer_addr, BD_ADDR_LEN)==0)) + { + OBX_TRACE_DEBUG3("[%d] state: %d, BDA: %08x", xx, p_spndcb->state, + (p_spndcb->peer_addr[2]<<24)+(p_spndcb->peer_addr[3]<<16)+(p_spndcb->peer_addr[4]<<8)+p_spndcb->peer_addr[5]); + /* this entry is not used yet or overwriting the entry with the same address */ + found = TRUE; + break; + } + else if (p_spndcb->stle.param == 0) + { + infinite = TRUE; + } + } + OBX_TRACE_DEBUG2("found: %d infinite:%d", found, infinite); + if (found == FALSE) + found = infinite; + } + else if (p_cb->max_suspend > 0) + { + found = TRUE; + } + p_ret = (tOBX_SPND_CB *)p_scb; + } + else if (p_cb->p_suspend) + { + ind = obx_read_triplet(p_triplet, num, OBX_TAG_SESS_PARAM_SESS_ID); + if (ind < num && p_triplet[ind].len == OBX_SESSION_ID_SIZE) + { + p = p_triplet[ind].p_array; + for (xx=0, p_spndcb=p_cb->p_suspend; xx<p_cb->max_suspend; xx++, p_spndcb++) + { + OBX_TRACE_DEBUG5("[%d] state: %d/%d, ssn:%d offset:x%x", xx, p_spndcb->state, + p_spndcb->sess_info[OBX_SESSION_INFO_ST_IDX], p_spndcb->ssn, p_spndcb->offset); + if ((p_spndcb->state != OBX_SS_NULL) && + (memcmp (p, p_spndcb->sess_info, OBX_SESSION_ID_SIZE) == 0)) + { + obxu_dump_hex (p_spndcb->sess_info, "sess info", OBX_SESSION_INFO_SIZE); + /* prepare p_scb to the proper state for resume */ + p_ret = p_spndcb; + break; + } + } + } + } + return p_ret; +} + +/******************************************************************************* +** Function obx_sr_sess_timeout +** Description Get suspended session control block from timer param. +** mark the suspended session as NULL +*******************************************************************************/ +void obx_sr_sess_timeout(TIMER_LIST_ENT *p_tle) +{ + tOBX_SPND_CB *p_spndcb = (tOBX_SPND_CB *) p_tle->param; + + OBX_TRACE_DEBUG0("obx_sr_sess_timeout"); + p_spndcb->state = OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sr_timeout +** Description Get server control block from timer param. Start BTU timer again. +** Call application callback routine with OBX_TIMEOUT_EVT event. +*******************************************************************************/ +void obx_sr_timeout(TIMER_LIST_ENT *p_tle) +{ + tOBX_SR_SESS_CB *p_scb = (tOBX_SR_SESS_CB *) p_tle->param; + tOBX_EVT_PARAM evtp; + tOBX_HANDLE handle = p_scb->ll_cb.comm.handle; + tOBX_SR_CBACK *p_cback; + tOBX_SR_CB *p_cb; + + memset(&evtp, 0, sizeof(tOBX_EVT_PARAM)); + if (obx_cb.timeout_val) + btu_start_timer(p_tle, BTU_TTYPE_OBX_SERVER_TO, obx_cb.timeout_val); + p_cb = &obx_cb.server[p_scb->handle - 1]; + p_cback = p_cb->p_cback; + obx_ssm_event(p_scb, OBX_TIMEOUT_SEVT, NULL); + (*p_cback) (handle, OBX_TIMEOUT_EVT, evtp, NULL); +} + +/******************************************************************************* +** +** Function obx_sr_alloc_cb +** +** Description allocate a server control block. +** +*******************************************************************************/ +tOBX_HANDLE obx_sr_alloc_cb(tOBX_StartParams *p_params) +{ + int xx, yy, zz; + tOBX_SR_CB *p_cb = &obx_cb.server[0]; + tOBX_SR_SESS_CB *p_scb = &obx_cb.sr_sess[0]; + tOBX_HANDLE obx_handle = OBX_HANDLE_NULL; + + OBX_TRACE_DEBUG1("obx_sr_alloc_cb num sess: %d", p_params->max_sessions); + /* allocate a server control block */ + for (xx=0; xx<obx_cb.num_server; xx++, p_cb++) + { + if (p_cb->scn == 0 && p_cb->psm == 0) + { + obx_handle = xx + 1; + p_cb->scn = p_params->scn; + p_cb->psm = p_params->psm; + break; + } + } + + if (xx != obx_cb.num_server) + { + /* allocate session control blocks */ + zz = 0; + for (yy=0; yy<obx_cb.num_sr_sess && zz < p_params->max_sessions; yy++, p_scb++) + { + if (p_scb->ll_cb.comm.handle == OBX_HANDLE_NULL) + { + p_scb->handle = obx_handle; + if (p_params->get_nonf) + p_scb->srmp = OBX_SRMP_NONF_EVT; + p_cb->sess[zz] = yy + 1; + p_scb->srm = p_params->srm; + p_scb->ll_cb.comm.handle = OBX_ENC_SESS_HANDLE((obx_handle), zz); + p_scb->ll_cb.comm.tx_mtu = 0; + p_scb->ll_cb.comm.tle.param = (UINT32)p_scb; + OBX_TRACE_DEBUG2("[%d]: 0x%x", zz, p_scb->ll_cb.comm.handle); + zz++; + } + } + + if (zz == p_params->max_sessions && L2C_IS_VALID_PSM(p_params->psm)) + { + if (obx_l2c_sr_register(p_cb) != OBX_SUCCESS) + { + OBX_TRACE_ERROR0("Cannot register to L2CAP"); + zz = 0; /* let it fail */ + } + } + + if (zz != p_params->max_sessions) + { + OBX_TRACE_ERROR0("not enough resources: release the allocated ones"); + p_cb->scn = 0; + p_cb->psm = 0; + obx_handle = OBX_HANDLE_NULL; + p_scb = &obx_cb.sr_sess[0]; + for (yy=0; yy<obx_cb.num_sr_sess; yy++, p_scb++) + { + if (p_scb->handle == obx_handle) + { + p_scb->ll_cb.comm.handle = OBX_HANDLE_NULL; + } + } + } + else + { + p_cb->num_sess = p_params->max_sessions; + p_cb->nonce = p_params->nonce; + p_cb->max_suspend = p_params->max_suspend; + if (p_cb->nonce && (p_cb->max_suspend == 0)) + p_cb->max_suspend = 1; + if (p_cb->max_suspend > OBX_MAX_SUSPEND_SESSIONS) + p_cb->max_suspend = OBX_MAX_SUSPEND_SESSIONS; + if ((p_cb->max_suspend * sizeof (tOBX_SPND_CB)) > GKI_MAX_BUF_SIZE) + { + OBX_TRACE_ERROR1("OBX_MAX_SUSPEND_SESSIONS:%d is too big", OBX_MAX_SUSPEND_SESSIONS); + } + } + } + return obx_handle; +} + +/******************************************************************************* +** +** Function obx_sr_get_cb +** +** Description Returns the pointer to the server control block with given handle. +** +*******************************************************************************/ +tOBX_SR_CB * obx_sr_get_cb(tOBX_HANDLE handle) +{ + tOBX_SR_CB *p_cb = NULL; + tOBX_HANDLE obx_handle = OBX_DEC_HANDLE(handle); + + /* check range */ + if (obx_handle <= obx_cb.num_server && obx_handle > 0 && obx_cb.server[obx_handle-1].p_cback) + { + p_cb = &obx_cb.server[obx_handle-1]; + } + + return p_cb; +} + +/******************************************************************************* +** +** Function obx_sr_get_scb +** +** Description Returns the pointer to the server control block with given handle. +** +*******************************************************************************/ +tOBX_SR_SESS_CB * obx_sr_get_scb(tOBX_HANDLE handle) +{ + tOBX_SR_CB *p_cb; + tOBX_SR_SESS_CB *p_scb = NULL; + tOBX_HANDLE obx_handle; + UINT16 sess_ind; + + /* check range */ + obx_handle = OBX_DEC_HANDLE(handle) - 1; + if (obx_handle < obx_cb.num_server) + { + /* make sure the handle is a valid one */ + p_cb = &obx_cb.server[obx_handle]; + sess_ind = OBX_DEC_SESS_IND(handle); + if ((sess_ind < p_cb->num_sess)) + { + if ((obx_cb.sr_sess[p_cb->sess[sess_ind]-1].ll_cb.comm.handle) == handle) + p_scb = &obx_cb.sr_sess[p_cb->sess[sess_ind]-1]; + } + } + + return p_scb; +} + +/******************************************************************************* +** +** Function obx_sr_free_cb +** +** Description Free the given server control block. +** +** Returns void +** +*******************************************************************************/ +void obx_sr_free_cb(tOBX_HANDLE handle) +{ + tOBX_SR_CB * p_cb = obx_sr_get_cb(handle); + tOBX_SR_SESS_CB *p_scb; + int yy; + + OBX_TRACE_DEBUG1("obx_sr_free_cb handle:0x%x", handle); + /* check range */ + if (p_cb) + { + p_scb = &obx_cb.sr_sess[0]; + for (yy=0; yy<obx_cb.num_sr_sess; yy++, p_scb++) + { + if (OBX_DEC_HANDLE(p_scb->handle) == OBX_DEC_HANDLE(handle)) + { + obx_sr_free_scb(p_scb); + if (p_scb->ll_cb.comm.id>0) + { + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + obx_cb.hdl_map[p_scb->ll_cb.port.port_handle - 1] = 0; + else + obx_cb.l2c_map[p_scb->ll_cb.l2c.lcid - L2CAP_BASE_APPL_CID] = 0; + } + memset(p_scb, 0, sizeof(tOBX_SR_SESS_CB) ); + } + } + + if (p_cb->p_auth) + { + GKI_freebuf(p_cb->p_auth); + p_cb->p_auth = 0; + } + + memset(p_cb, 0, sizeof(tOBX_SR_CB) ); + } +} + +/******************************************************************************* +** +** Function obx_sr_free_scb +** +** Description Free the given server session control block. +** +** Returns void +** +*******************************************************************************/ +void obx_sr_free_scb(tOBX_SR_SESS_CB *p_scb) +{ + OBX_TRACE_DEBUG2("obx_sr_free_scb shandle:0x%x, sess_st:%d", p_scb->ll_cb.comm.handle, p_scb->sess_st); + + /* make sure the GKI buffers are freed */ + if (p_scb->p_saved_msg) + { + GKI_freebuf(p_scb->p_saved_msg); + p_scb->p_saved_msg = 0; + } + + if (p_scb->p_next_req) + { + GKI_freebuf(p_scb->p_next_req); + p_scb->p_next_req = 0; + } + obx_free_buf (&p_scb->ll_cb); + + /* make sure the timer is stopped */ + btu_stop_timer(&p_scb->ll_cb.comm.tle); +} + +/******************************************************************************* +** +** Function obx_sr_get_next_conn_id +** +** Description assigns the next available connection id. It will avoid using +** active conn id instances as well so that we can work with the +** IVT stack bugs. +** +*******************************************************************************/ +UINT32 obx_sr_get_next_conn_id(void) +{ +#if (OBX_CLIENT_INCLUDED == TRUE) + tOBX_CL_CB *p_ccb; + int xx; + BOOLEAN done; + + /* Make sure no client instances are using the value */ + do + { + obx_cb.next_cid++; + + /* Increment the value and make sure it is legal */ + if (obx_cb.next_cid == OBX_INVALID_CONN_ID) + obx_cb.next_cid = OBX_INITIAL_CONN_ID; + + done = TRUE; + for (xx=0, p_ccb = &obx_cb.client[0]; xx < obx_cb.num_client; xx++, p_ccb++) + { + /* If the handle is in use and same as proposed conn_id, increment and restart */ + if (p_ccb->ll_cb.comm.handle != OBX_HANDLE_NULL && p_ccb->conn_id == obx_cb.next_cid) + { + OBX_TRACE_WARNING1(" **** OBX CONN_ID Collision (0x%08x) trying another...", obx_cb.next_cid); + done = FALSE; + break; + } + } + } while (!done); + +#else /* Client code is not compiled in */ + + obx_cb.next_cid++; + + /* Increment the value and make sure it is legal */ + if (obx_cb.next_cid == OBX_INVALID_CONN_ID) + obx_cb.next_cid = OBX_INITIAL_CONN_ID; +#endif + + return obx_cb.next_cid; +} + +#endif /* OBX_SERVER_INCLUDED */ + +/******************************************************************************* +** +** Function obx_port_handle_2cb +** +** Description Given a port handle, return the associated client or server +** control block. +** +** Returns +** +*******************************************************************************/ +tOBX_PORT_CB * obx_port_handle_2cb(UINT16 port_handle) +{ + tOBX_PORT_CB *p_pcb = NULL; + tOBX_HANDLE obx_handle = 0; +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_CB *p_cb; +#endif + + /* this function is called by obx_rfc_cback() only. + * assume that port_handle is within range */ + obx_handle = obx_cb.hdl_map[port_handle-1]; + OBX_TRACE_DEBUG2("obx_port_handle_2cb port_handle:%d obx_handle:0x%x", port_handle, obx_handle); + + if (obx_handle > 0) + { + if (obx_handle & OBX_CL_HANDLE_MASK) + { +#if (OBX_CLIENT_INCLUDED == TRUE) + obx_handle &= OBX_CL_CB_IND_MASK; + p_pcb = &obx_cb.client[obx_handle - 1].ll_cb.port; +#endif /* OBX_CLIENT_INCLUDED */ + } + else + { +#if (OBX_SERVER_INCLUDED == TRUE) + p_cb = &obx_cb.server[OBX_DEC_HANDLE(obx_handle) - 1]; + p_pcb = &obx_cb.sr_sess[p_cb->sess[OBX_DEC_SESS_IND(obx_handle)]-1].ll_cb.port; + OBX_TRACE_DEBUG1("p_pcb port_handle:%d", p_pcb->port_handle); +#endif /* OBX_SERVER_INCLUDED */ + } + } + + return p_pcb; +} + + + +/******************************************************************************* +** +** Function OBX_Init +** +** Description This function is called to initialize the control block for this +** layer. It must be called before accessing any other of its API +** functions. It is typically called once during the start up of +** the stack. +** +** Returns void. +** +*******************************************************************************/ +void OBX_Init(void) +{ + memset(&obx_cb, 0, sizeof(tOBX_CB)); + +#if defined(OBX_INITIAL_TRACE_LEVEL) + obx_cb.trace_level = OBX_INITIAL_TRACE_LEVEL; +#else + obx_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + +#if (OBX_CLIENT_INCLUDED == TRUE) + obx_cb.next_ind = 0; + obx_cb.num_client = OBX_NUM_CLIENTS; +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) + obx_cb.num_server = OBX_NUM_SERVERS; + obx_cb.num_sr_sess = OBX_NUM_SR_SESSIONS; + obx_cb.next_cid = OBX_INITIAL_CONN_ID; +#endif + obx_cb.timeout_val = OBX_TIMEOUT_VALUE; + obx_cb.sess_tout_val= OBX_SESS_TIMEOUT_VALUE; + obx_cb.max_rx_qcount= OBX_MAX_RX_QUEUE_COUNT; +} + +/******************************************************************************* +** +** Function OBX_SetTraceLevel +** +** Description This function sets the debug trace level for OBX. +** If 0xff is passed, the current trace level is returned. +** +** Input Parameters: +** level: The level to set the OBX tracing to: +** 0xff-returns the current setting. +** 0-turns off tracing. +** >= 1-Errors. +** >= 2-Warnings. +** >= 3-APIs. +** >= 4-Events. +** >= 5-Debug. +** +** Returns The new trace level or current trace level if +** the input parameter is 0xff. +** +*******************************************************************************/ +UINT8 OBX_SetTraceLevel (UINT8 level) +{ + if (level != 0xFF) + obx_cb.trace_level = level; + + return (obx_cb.trace_level); +} + +/******************************************************************************* +** Function OBX_HandleToMtu +** +** Description Given an OBEX handle, return the associated peer MTU. +** +** Returns MTU. +** +*******************************************************************************/ +UINT16 OBX_HandleToMtu(tOBX_HANDLE handle) +{ + UINT16 mtu = OBX_MIN_MTU; + BOOLEAN rx = (handle & OBX_HANDLE_RX_MTU_MASK); +#if (OBX_CLIENT_INCLUDED == TRUE) + int xx; +#endif +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_SESS_CB *p_scb; +#endif + + handle &= ~OBX_HANDLE_RX_MTU_MASK; + + if (handle != OBX_HANDLE_NULL) + { + if (handle & OBX_CL_HANDLE_MASK) + { +#if (OBX_CLIENT_INCLUDED == TRUE) + /* a client handle */ + for (xx=0; xx<obx_cb.num_client; xx++) + { + if (handle == obx_cb.client[xx].ll_cb.comm.handle) + { + mtu = (rx) ? obx_cb.client[xx].ll_cb.comm.rx_mtu : obx_cb.client[xx].ll_cb.comm.tx_mtu; + break; + } + } +#endif + } + else + { +#if (OBX_SERVER_INCLUDED == TRUE) + /* a server handle */ + p_scb = obx_sr_get_scb(handle); + /* make sure the handle is a valid one */ + if (p_scb && p_scb->ll_cb.comm.handle != OBX_HANDLE_NULL) + { + mtu = (rx) ? p_scb->ll_cb.comm.rx_mtu : p_scb->ll_cb.comm.tx_mtu; + } +#endif + } + } + + if (mtu < OBX_MIN_MTU) + mtu = OBX_MIN_MTU; + OBX_TRACE_DEBUG3("OBX_HandleToMtu handle: 0x%x, rx:%x, mtu:%d", handle, rx, mtu); + return mtu; +} + diff --git a/stack/obx/obx_md5.c b/stack/obx/obx_md5.c new file mode 100644 index 0000000..6e0d6ea --- /dev/null +++ b/stack/obx/obx_md5.c @@ -0,0 +1,1050 @@ +/***************************************************************************** +** +** Name: obx_md5.c +** +** File: OBEX Authentication related functions +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <stdio.h> +#include <string.h> + +#include "bt_target.h" +#include "btu.h" +#include "obx_int.h" +#include "wcassert.h" + + + +/* +* This code implements the MD5 message-digest algorithm. +* The algorithm is due to Ron Rivest. This code was +* written by Colin Plumb in 1993, no copyright is claimed. +* This code is in the public domain; do with it what you wish. +* +* Equivalent code is available from RSA Data Security, Inc. +* This code has been tested against that, and is equivalent, +* except that you don't need to include two pages of legalese +* with every copy. +* +* To compute the message digest of a chunk of bytes, declare an +* MD5Context structure, pass it to MD5Init, call MD5Update as +* needed on buffers full of bytes, and then call MD5Final, which +* will fill a supplied 16-byte array with the digest. +*/ +typedef UINT32 word32; +typedef UINT8 byte; +struct xMD5Context { + word32 buf[4]; + word32 bytes[2]; + word32 in[16]; +}; + +static void obx_md5_transform(word32 buf[4], word32 const in[16]); + +/* +* Shuffle the bytes into little-endian order within words, as per the +* MD5 spec. Note: this code works regardless of the byte order. +*/ +static void obx_byte_swap(word32 *buf, unsigned words) +{ + byte *p = (byte *)buf; + do { + *buf++ = (word32)((unsigned)p[3] << 8 | p[2]) << 16 + | (word32)((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} + +/* +* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious +* initialization constants. +*/ +static void obx_md5_init(struct xMD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* +* Update context to reflect the concatenation of another buffer full +* of bytes. +*/ +static void obx_md5_update(struct xMD5Context *ctx, byte const *buf, int len) +{ + word32 t; + /* Update byte count */ + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + t = 64 - (t & 0x3f); /* Space avail in ctx->in (at least 1) */ + if ((unsigned)t > (unsigned)len) + { + memcpy ((byte *)ctx->in + 64 - (unsigned)t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy (ctx->in + (64 - (unsigned)t), buf, (unsigned)t); + obx_byte_swap(ctx->in, 16); + obx_md5_transform(ctx->buf, ctx->in); + buf += (unsigned)t; + len -= (unsigned)t; + /* Process data in 64-byte chunks */ + while (len >= 64) + { + /* coverity[access_dbuff_const] */ + memcpy (ctx->in, buf, 64); + obx_byte_swap(ctx->in, 16); + obx_md5_transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + /* Handle any remaining bytes of data. */ + memcpy (ctx->in, buf, len); +} + +/* +* Final wrapup - pad to 64-byte boundary with the bit pattern +* 1 0* (64-bit count of bits processed, MSB-first) +*/ +static void obx_md5_final(byte digest[16], struct xMD5Context *ctx) +{ + int count = (int)(ctx->bytes[0] & 0x3f); /* Bytes in ctx->in */ + byte *p = (byte *)ctx->in + count; /* First unused byte */ + /* Set the first char of padding to 0x80. There is always room.*/ + *p++ = 0x80; + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + if (count < 0) /* Padding forces an extra block */ + { + memset (p, 0, count+8); + obx_byte_swap(ctx->in, 16); + obx_md5_transform(ctx->buf, ctx->in); + p = (byte *)ctx->in; + count = 56; + } + memset (p, 0, count+8); + obx_byte_swap(ctx->in, 14); + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + obx_md5_transform(ctx->buf, ctx->in); + obx_byte_swap(ctx->buf, 4); + memcpy (digest, ctx->buf, 16); + memset (ctx, 0, sizeof(ctx)); +} + +/* The four core functions - F1 is optimized somewhat */ +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ +(w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) + +/* +* The core of the MD5 algorithm, this alters an existing MD5 hash to +* reflect the addition of 16 longwords of new data. MD5Update blocks +* the data and converts bytes into longwords for this routine. +*/ +static const word32 obx_md5_f1 [] = +{ + 0xd76aa478, + 0xe8c7b756, + 0x242070db, + 0xc1bdceee, + + 0xf57c0faf, + 0x4787c62a, + 0xa8304613, + 0xfd469501, + + 0x698098d8, + 0x8b44f7af, + 0xffff5bb1, + 0x895cd7be, + + 0x6b901122, + 0xfd987193, + 0xa679438e, + 0x49b40821 +}; + +static const word32 obx_md5_f2 [] = +{ + 0xf61e2562, + 0xc040b340, + 0x265e5a51, + 0xe9b6c7aa, + + 0xd62f105d, + 0x02441453, + 0xd8a1e681, + 0xe7d3fbc8, + + 0x21e1cde6, + 0xc33707d6, + 0xf4d50d87, + 0x455a14ed, + + 0xa9e3e905, + 0xfcefa3f8, + 0x676f02d9, + 0x8d2a4c8a +}; + +static const word32 obx_md5_f3 [] = +{ + 0xfffa3942, + 0x8771f681, + 0x6d9d6122, + 0xfde5380c, + + 0xa4beea44, + 0x4bdecfa9, + 0xf6bb4b60, + 0xbebfbc70, + + 0x289b7ec6, + 0xeaa127fa, + 0xd4ef3085, + 0x04881d05, + + 0xd9d4d039, + 0xe6db99e5, + 0x1fa27cf8, + 0xc4ac5665 +}; + +static const word32 obx_md5_f4 [] = +{ + 0xf4292244, + 0x432aff97, + 0xab9423a7, + 0xfc93a039, + + 0x655b59c3, + 0x8f0ccc92, + 0xffeff47d, + 0x85845dd1, + + 0x6fa87e4f, + 0xfe2ce6e0, + 0xa3014314, + 0x4e0811a1, + + 0xf7537e82, + 0xbd3af235, + 0x2ad7d2bb, + 0xeb86d391 +}; + +static const UINT8 obx_md5_a [] = +{ + 1, + 2, + 3, + 0, + 1, + 2, + 3 +}; + +static const word32 obx_md5_var1 [] = +{ + 7, + 12, + 17, + 22 +}; + +static const word32 obx_md5_var2 [] = +{ + 5, + 9, + 14, + 20 +}; + +static const word32 obx_md5_var3 [] = +{ + 4, + 11, + 16, + 23 +}; + +static const word32 obx_md5_var4 [] = +{ + 6, + 10, + 15, + 21 +}; + +static void obx_md5_transform(word32 buf[4], word32 const in[16]) +{ + int xx, yy, zz, i, j, k; + word32 a[4]; + + a[0] = buf[0]; + a[1] = buf[1]; + a[2] = buf[2]; + a[3] = buf[3]; + + yy = 0; + for(xx=0; xx<4; xx++) + { + j = 3; + for(i=0; i < 4; i++) + { + k = j--; + /* + OBX_TRACE_DEBUG4( "f1 a: %d, yy: %d, inc: 0x%x, var: %d", + obx_md5_a[k], yy, obx_md5_f1[yy], obx_md5_var1[i]); + */ + MD5STEP(F1, a[obx_md5_a[k]], a[obx_md5_a[k+1]], a[obx_md5_a[k+2]], a[obx_md5_a[k+3]], + in[yy] + obx_md5_f1[yy], obx_md5_var1[i]); + yy++; + } + } + + yy = 1; + zz = 0; + for(xx=0; xx<4; xx++) + { + j = 3; + for(i=0; i < 4; i++) + { + k = j--; + /* + OBX_TRACE_DEBUG4( "f2 a: %d, yy: %d, inc: 0x%x, var: %d", + obx_md5_a[k], yy, obx_md5_f2[zz], obx_md5_var2[i]); + */ + MD5STEP(F2, a[obx_md5_a[k]], a[obx_md5_a[k+1]], a[obx_md5_a[k+2]], a[obx_md5_a[k+3]], + in[yy] + obx_md5_f2[zz++], obx_md5_var2[i]); + yy += 5; + yy %= 16; + } + } + + yy = 5; + zz = 0; + for(xx=0; xx<4; xx++) + { + j = 3; + for(i=0; i < 4; i++) + { + k = j--; + /* + OBX_TRACE_DEBUG4( "f3 a: %d, yy: %d, inc: 0x%x, var: %d", + obx_md5_a[k], yy, obx_md5_f3[zz], obx_md5_var3[i]); + */ + MD5STEP(F3, a[obx_md5_a[k]], a[obx_md5_a[k+1]], a[obx_md5_a[k+2]], a[obx_md5_a[k+3]], + in[yy] + obx_md5_f3[zz++], obx_md5_var3[i]); + yy += 3; + yy %= 16; + } + } + + + yy = 0; + zz = 0; + for(xx=0; xx<4; xx++) + { + j = 3; + for(i=0; i < 4; i++) + { + k = j--; + /* + OBX_TRACE_DEBUG4( "f4 a: %d, yy: %d, inc: 0x%x, var: %d", + obx_md5_a[k], yy, obx_md5_f4[zz], obx_md5_var4[i]); + */ + MD5STEP(F4, a[obx_md5_a[k]], a[obx_md5_a[k+1]], a[obx_md5_a[k+2]], a[obx_md5_a[k+3]], + in[yy] + obx_md5_f4[zz++], obx_md5_var4[i]); + yy += 7; + yy %= 16; + } + } + + buf[0] += a[0]; + buf[1] += a[1]; + buf[2] += a[2]; + buf[3] += a[3]; +} + +/******************************************************************************* +** +** Function OBX_MD5 +** +** Description This function is called to run the MD5 algorithm. +** +** Returns void +** +*******************************************************************************/ +static void OBX_MD5(void *digest, UINT8 *nonce, UINT8 * password, int password_len) +{ + struct xMD5Context context; + UINT8 before[OBX_NONCE_SIZE + OBX_MAX_AUTH_KEY_SIZE + 4]; + + memcpy(before, nonce, OBX_NONCE_SIZE); + before[OBX_NONCE_SIZE] = ':'; + memcpy(before + OBX_NONCE_SIZE + 1, password, password_len); + /* + scru_dump_hex (before, "before", OBX_NONCE_SIZE + 1 + password_len, TRACE_LAYER_OBEX, TRACE_TYPE_GENERIC); + */ + + obx_md5_init(&context); +/* coverity[overrun-buffer-val] */ +/* +FALSE-POSITIVE: coverity says "Overrun of static array "before" of size 36 bytes by passing it as an argument to a function which indexes it at byte position 63" +obx_md5_update() only goes into that condition when (len >= 64). In this case, len is (OBX_NONCE_SIZE + 1 + password_len) which is less than or equal to 33. +password_len is less than or equal to OBX_MAX_AUTH_KEY_SIZE. The size of before[] is more than enough */ + obx_md5_update(&context, (byte const *)before, OBX_NONCE_SIZE + 1 + password_len); + obx_md5_final((byte *)digest, &context); + /* + scru_dump_hex (digest, "after", 16, TRACE_LAYER_OBEX, TRACE_TYPE_GENERIC); + */ +} + +/******************************************************************************* +** +** Function obx_session_id +** +** Description This function is called to run the MD5 algorithm to create a +** session id. +** +** Returns void +** +*******************************************************************************/ +void obx_session_id(UINT8 *p_sess_id, UINT8 *p_cl_addr, UINT8 * p_cl_nonce, int cl_nonce_len, + UINT8 *p_sr_addr, UINT8 * p_sr_nonce, int sr_nonce_len) +{ + struct xMD5Context context; + UINT8 before[(OBX_NONCE_SIZE + BD_ADDR_LEN) * 2 + 4]; + UINT8 *p = before; + UINT8 len; + + memcpy(p, p_cl_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + memcpy(p, p_cl_nonce, cl_nonce_len); + p += cl_nonce_len; + memcpy(p, p_sr_addr, BD_ADDR_LEN); + p += BD_ADDR_LEN; + memcpy(p, p_sr_nonce, sr_nonce_len); + p += sr_nonce_len; + /* + scru_dump_hex (before, "before", OBX_NONCE_SIZE + 1 + password_len, TRACE_LAYER_OBEX, TRACE_TYPE_GENERIC); + */ + + len = BD_ADDR_LEN + cl_nonce_len + BD_ADDR_LEN + sr_nonce_len; + obx_md5_init(&context); +/* coverity [overrun-buffer-val] */ +/* Overrun of static array "before" of size 48 bytes by passing it as an argument to a function which indexes it at byte position 63*/ +/* FALSE-POSITIVE: obx_md5_update() only goes into that condition when (len >= 64). In this case, len is (OBX_NONCE_SIZE + 1 + password_len) which is less than or equal to 33. +password_len is less than or equal to OBX_MAX_AUTH_KEY_SIZE. The size of before[] is more than enough */ + obx_md5_update(&context, (byte const *)before, len); + obx_md5_final((byte *)p_sess_id, &context); + /* + scru_dump_hex (p_sess_id, "after", 16, TRACE_LAYER_OBEX, TRACE_TYPE_GENERIC); + */ +} + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** +** Function obx_copy_and_rm_auth_hdrs +** +** Description This function is used to copy the given OBEX packet to a new packet +** with the authentication headers removed. +** +** Returns tOBX_STATUS +** +*******************************************************************************/ +tOBX_STATUS obx_copy_and_rm_auth_hdrs(BT_HDR *p_pkt, BT_HDR *p_new, UINT8 * p_hi) +{ + tOBX_STATUS status = OBX_SUCCESS; + UINT8 *pn, *po, *p_nlen = NULL; + int hsize, nsize; + UINT16 nlen, olen; + UINT8 size; + int xx; + + WC_ASSERT(p_new); + + + po = (UINT8 *)(p_pkt + 1) + p_pkt->offset + 1; + BE_STREAM_TO_UINT16(olen, po); + OBX_TRACE_DEBUG3( "obx_copy_and_rm_auth_hdrs event: %d, new len:%d, olen:%d", + p_pkt->event, p_new->len, olen); + + po = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + if(p_new->len == 0) + { + p_new->offset = p_pkt->offset; + p_nlen = (UINT8 *)(p_new + 1) + p_new->offset + 1; + } + pn = (UINT8 *)(p_new + 1) + p_new->offset; + + /* Get the number of bytes before headers start */ + size = obx_hdr_start_offset[p_pkt->event - 1]; + if(p_new->len) + { + /* for OBX_AuthResponse + * - do not need to copy the stuff before headers + * there's already some header, skip them */ + pn += p_new->len; + nlen = p_new->len; + } + else + { + /* for obx_unauthorize_rsp + * - needs to copy the stuff before headers */ + memcpy(pn, po, size); + pn += size; + nlen = size; + } + po += size; + + olen -= size; + while(olen) + { + OBX_TRACE_DEBUG1( "olen:%d", olen); + hsize = (int)obx_read_header_len(po); + if(olen > hsize) + olen -= hsize; + else + olen = 0; + + xx = 0; + nsize = hsize; + while(p_hi[xx] != OBX_HI_NULL) + { + /* + OBX_TRACE_DEBUG2( "po:0x%x, hix:0x%x", *po, p_hi[xx]); + */ + if(*po == p_hi[xx++]) + { + nsize = 0; + break; + } + } + OBX_TRACE_DEBUG2( "hsize:%d, nsize:%d", hsize, nsize); + if(nsize) + { + /* skip auth challenge and auth response */ + if((nlen+hsize) < p_pkt->layer_specific) + { + /* copy other headers */ + memcpy(pn, po, hsize); + pn += hsize; + nlen += hsize; + } + else + { + OBX_TRACE_WARNING1( "obx_copy_and_rm_auth_hdrs Not enough room: %d", olen); + /* no more room in the new packet */ + status = OBX_NO_RESOURCES; + break; + } + } + po += hsize; + } + + if(status == OBX_SUCCESS) + { + if(p_nlen) + { + UINT16_TO_BE_STREAM(p_nlen, nlen); + } + p_new->len = nlen; + p_new->layer_specific = GKI_get_buf_size(p_new) - BT_HDR_SIZE - p_new->offset; + p_new->event = p_pkt->event; + } + + return status; +} + +/******************************************************************************* +** +** Function obx_unauthorize_rsp +** +** Description This function is used to add authentication challenge triplet +** +** Returns void. +** Note: this function assumes that all data can fit in the MTU +*******************************************************************************/ +BT_HDR * obx_unauthorize_rsp(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + UINT8 *p; + UINT8 nonce[OBX_NONCE_SIZE+2]; + UINT8 option[2]; + UINT8 *p_target = NULL; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 num_trip = 0; + BT_HDR *p_old; + UINT8 hi[] = {OBX_HI_CHALLENGE, OBX_HI_AUTH_RSP, OBX_HI_NULL}; + + OBX_TRACE_DEBUG1( "obx_unauthorize_rsp: %d", p_cb->target.len); + if(OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) || OBX_CheckHdr(p_pkt, OBX_HI_AUTH_RSP)) + { + p_old = p_pkt; + p_pkt = OBX_HdrInit(p_scb->ll_cb.comm.handle, (UINT16)(p_old->len + BT_HDR_SIZE + p_old->offset)); + obx_copy_and_rm_auth_hdrs(p_old, p_pkt, hi); + GKI_freebuf(p_old); + } + + if(p_cb->target.len) + { + /* There must be the target header, change it to be WHO header */ + if( (p_target = OBX_CheckHdr(p_pkt, OBX_HI_TARGET)) != NULL) + { + *p_target = OBX_HI_WHO; + } + } + OBX_TRACE_DEBUG1( "p_target: %x", p_target); + + if(p_cb->p_auth) + { + /* server registers for authentication */ + /* prepare the triplet byte sequence */ + /* add nonce: tag, length, value */ + p_cb->p_auth->nonce = GKI_get_tick_count(); + sprintf ((char *)nonce, "%016lu", p_cb->p_auth->nonce); + + num_trip = 0; + triplet[num_trip].tag = OBX_NONCE_CHLNG_TAG; + triplet[num_trip].len = OBX_NONCE_SIZE; + triplet[num_trip].p_array = nonce; + num_trip++; + + if(p_cb->p_auth->auth_option) + { + /* add option */ + triplet[num_trip].tag = OBX_OPTIONS_CHLNG_TAG; + triplet[num_trip].len = 1; + option[0] = p_cb->p_auth->auth_option; + triplet[num_trip].p_array = option; + num_trip++; + } + if(p_cb->p_auth->realm_len) + { + /* add realm */ + triplet[num_trip].tag = OBX_REALM_CHLNG_TAG; + triplet[num_trip].len = p_cb->p_auth->realm_len+1; + triplet[num_trip].p_array = p_cb->p_auth->realm; + num_trip++; + } + + /* add the sequence to header */ + OBX_AddTriplet(p_pkt, OBX_HI_CHALLENGE, triplet, num_trip); + } + + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset; + /* change the opcode to unauthorized response */ + *p++ = OBX_RSP_UNAUTHORIZED | OBX_FINAL; + /* adjust the packet len */ + UINT16_TO_BE_STREAM(p, p_pkt->len); + + /* add session sequence number, if session is active */ + if ((p_scb->sess_st == OBX_SESS_ACTIVE) && ((p_target = OBX_CheckHdr(p_pkt, OBX_HI_SESSION_SN)) != NULL)) + { + p_target++; + (*p_target)++; + } + + p_pkt->event = OBX_CONNECT_RSP_EVT; + + return p_pkt; +} + +/******************************************************************************* +** +** Function OBX_Password +** +** Description This function is called by the server to respond to an +** OBX_PASSWORD_EVT event. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_Password(tOBX_HANDLE shandle, UINT8 *p_password, UINT8 password_len, + UINT8 *p_userid, UINT8 userid_len) +{ + tOBX_STATUS status = OBX_SUCCESS; + BT_HDR *p_pkt; + BT_HDR *p_rsp; + UINT8 temp_digest[OBX_DIGEST_SIZE]; + UINT8 nonce[OBX_NONCE_SIZE + 1]; + BOOLEAN pass = FALSE; + BOOLEAN digest_done = FALSE; + BOOLEAN option_userid = FALSE; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + UINT8 xx; + UINT8 *p_target; + UINT16 tlen; + tOBX_SR_SESS_CB *p_scb = obx_sr_get_scb(shandle); + tOBX_SR_CB *p_cb = obx_sr_get_cb(shandle); + UINT8 *p; + tOBX_EVT_PARAM param; /* The event parameter. */ + + WC_ASSERT(password_len < OBX_MAX_AUTH_KEY_SIZE); + + if(p_scb == NULL || p_scb->p_saved_msg == NULL) + return OBX_NO_RESOURCES; + + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if num_trip returns TRUE. + * leave this unnecessary memset here */ + memset(triplet,0,sizeof(triplet)); + + p_pkt = p_scb->p_saved_msg; + + if(p_cb->p_auth == NULL) + { + pass = TRUE; + } + else if(OBX_ReadTriplet(p_pkt, OBX_HI_AUTH_RSP, triplet, &num_trip) == TRUE) + { + if(p_password && password_len ) + { + /* if given password, verify it */ + for(xx=0; xx<num_trip; xx++) + { + if(triplet[xx].tag == OBX_DIGEST_RSP_TAG) + { + sprintf((char *)nonce, "%016lu", p_cb->p_auth->nonce); + OBX_MD5 (temp_digest, nonce, p_password, password_len); + if (memcmp (temp_digest, triplet[xx].p_array, OBX_DIGEST_SIZE) == 0) + pass = TRUE; + break; + } + } + } + } + + /* check if challenged by client */ + num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + if(pass == TRUE && OBX_ReadTriplet(p_pkt, OBX_HI_CHALLENGE, triplet, &num_trip) == TRUE) + { + /* Make sure password was passed in and not empty */ + if (p_password && password_len ) + { + for(xx=0; xx<num_trip; xx++) + { + if(triplet[xx].tag == OBX_NONCE_CHLNG_TAG) + { + /* nonce tag is mandatory */ + OBX_MD5 (temp_digest, triplet[xx].p_array, p_password, password_len); + digest_done = TRUE; + } + else if(triplet[xx].tag == OBX_OPTIONS_CHLNG_TAG) + { + /* user ID bit is set */ + if(OBX_AO_USR_ID & triplet[xx].p_array[0]) + option_userid = TRUE; + } + } + + if (option_userid && (userid_len == 0 || p_userid == NULL) ) + { + status = OBX_BAD_PARAMS; + } + } + } + + if(status == OBX_SUCCESS) + { + p_scb->p_saved_msg = NULL; + if(pass == TRUE) + { + p_rsp = OBX_HdrInit(shandle, OBX_MIN_MTU); + + /* add the authentication response if been challenged */ + if(digest_done) + { + num_trip = 1; + triplet[0].tag = OBX_DIGEST_RSP_TAG; + triplet[0].len = OBX_DIGEST_SIZE; + triplet[0].p_array = temp_digest; + if(option_userid) + { + num_trip++; + triplet[1].tag = OBX_USERID_RSP_TAG; + triplet[1].len = userid_len; + triplet[1].p_array = p_userid; + } + OBX_AddTriplet(p_rsp, OBX_HI_AUTH_RSP, triplet, num_trip); + } + + if( p_cb->target.len == OBX_DEFAULT_TARGET_LEN && + OBX_ReadTargetHdr(p_pkt, &p_target, &tlen, 0) == TRUE) + { + /* API user is supposed to handle WHO header + * for this special case, we add it for the API user */ + OBX_AddWhoHdr(p_rsp, p_target, tlen); + } + + /* use OBX_ConnectRsp to fill the common data */ + status = OBX_ConnectRsp(shandle, OBX_RSP_OK, p_rsp); + if(status == OBX_SUCCESS) + { + /* If authentication is successful, need to update session info */ + p = &p_scb->sess_info[OBX_SESSION_INFO_ID_IDX]; + UINT32_TO_BE_STREAM(p, p_scb->conn_id); + param.sess.p_sess_info = p_scb->sess_info; + param.sess.sess_op = OBX_SESS_OP_CREATE; + param.sess.sess_st = p_scb->sess_st; + param.sess.nssn = p_scb->param.ssn; + param.sess.ssn = p_scb->param.ssn; + param.sess.obj_offset = 0; + p = &p_scb->sess_info[OBX_SESSION_INFO_MTU_IDX]; + UINT16_TO_BE_STREAM(p, p_scb->param.conn.mtu); + memcpy(param.sess.peer_addr, p_scb->peer_addr, BD_ADDR_LEN); + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_REQ_EVT, param, p_pkt); + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CONNECT_REQ_EVT, p_scb->param, p_pkt); + } + else + GKI_freebuf(p_pkt); + } + else + { + /* server can not find the password, send unauthorized response */ + p_pkt = obx_unauthorize_rsp(p_cb, p_scb, p_pkt); + obx_ssm_event(p_scb, OBX_CONNECT_CFM_SEVT, p_pkt); + } + } + /* + else p_scb->p_saved_msg is still valid. + */ + + return status; +} +#endif + + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function OBX_AuthResponse +** +** Description This function is called by the client to respond to an +** OBX_PASSWORD_EVT event. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not resources +** +*******************************************************************************/ +tOBX_STATUS OBX_AuthResponse(tOBX_HANDLE handle, + UINT8 *p_password, UINT8 password_len, + UINT8 *p_userid, UINT8 userid_len, + BOOLEAN authenticate) +{ + tOBX_CL_CB *p_cb = obx_cl_get_cb(handle); + tOBX_STATUS status = OBX_NO_RESOURCES; + BT_HDR *p_pkt; + BT_HDR *p_saved_req; + UINT8 num_trip, flags = 0; + UINT16 nlen, xx; + UINT8 temp_digest[OBX_DIGEST_SIZE]; + UINT8 nonce[OBX_NONCE_SIZE + 1]; + BOOLEAN digest_done = FALSE; + BOOLEAN option_userid = FALSE; + UINT8 rsp_code; + BOOLEAN final; + UINT8 *p_target; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 hi[] = {OBX_HI_CONN_ID, OBX_HI_TARGET, OBX_HI_CHALLENGE, OBX_HI_AUTH_RSP, + OBX_HI_SESSION_SN, /* prevent SSN from being copied */ + OBX_HI_NULL}; + WC_ASSERT(password_len < OBX_MAX_AUTH_KEY_SIZE); + WC_ASSERT(p_password); + + if(p_cb == NULL || p_cb->p_auth == NULL || p_cb->p_saved_req == NULL) + return OBX_NO_RESOURCES; + + p_saved_req = p_cb->p_saved_req; + p_pkt = OBX_HdrInit(handle, GKI_MAX_BUF_SIZE); /* make sure added length will fit */ + WC_ASSERT(p_pkt); + OBX_TRACE_DEBUG2("OBX_AuthResponse p_saved_req:%d, p_saved_req->len %d ", + p_saved_req->event, p_saved_req->len); + + /* if the save_req contains SSN, add it now */ + if ((p_target = OBX_CheckHdr(p_saved_req, OBX_HI_SESSION_SN)) != NULL) + { + p_target++; + OBX_Add1ByteHdr(p_pkt, OBX_HI_SESSION_SN, (UINT8)((*p_target) + 1)); + } + + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if num_trip returns TRUE. + * leave this unnecessary memset here */ + memset(triplet,0,sizeof(triplet)); + + /* if target header exists in the original request + * and the Connection ID has not been assigned yet, + * copy the target header over */ + if(OBX_ReadTargetHdr(p_saved_req, &p_target, &nlen, 0) == TRUE) + { + OBX_AddTargetHdr(p_pkt, p_target, nlen); + } + + /* read the challenge from received packet */ + num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + if(OBX_ReadTriplet(p_cb->p_auth, OBX_HI_CHALLENGE, triplet, &num_trip) == TRUE) + { + status = OBX_SUCCESS; + for(xx=0; xx<num_trip; xx++) + { + if(triplet[xx].tag == OBX_NONCE_CHLNG_TAG) + { + /* nonce tag is mandatory */ + OBX_MD5 (temp_digest, triplet[xx].p_array, p_password, password_len); + digest_done = TRUE; + } + else if(triplet[xx].tag == OBX_OPTIONS_CHLNG_TAG) + { + /* user ID bit is set */ + if(OBX_AO_USR_ID & triplet[xx].p_array[0]) + option_userid = TRUE; + } + } + + if(option_userid && (userid_len == 0 || p_userid == NULL) ) + { + status = OBX_BAD_PARAMS; + } + OBX_TRACE_DEBUG2( "num_trip:%d, option_userid:%d", num_trip, option_userid); + + if(digest_done && status == OBX_SUCCESS) + { + /* Compose and add the authentication Response header */ + num_trip = 1; + triplet[0].tag = OBX_DIGEST_RSP_TAG; + triplet[0].len = OBX_DIGEST_SIZE; + triplet[0].p_array = temp_digest; + if(option_userid) + { + /* add user id */ + num_trip++; + triplet[1].tag = OBX_USERID_RSP_TAG; + triplet[1].len = userid_len; + triplet[1].p_array = p_userid; + } + OBX_AddTriplet(p_pkt, OBX_HI_AUTH_RSP, triplet, num_trip); + + /* if we want to authenticate the server, add the challenge header here */ + /* Note: we only do it for the CONNECT request */ + if(authenticate && p_saved_req->event == OBX_CONNECT_REQ_EVT) + { + /* Indicate in the control block to verify the response */ + p_cb->wait_auth = TRUE; + + /* add the challenge nonce */ + num_trip = 1; + triplet[0].tag = OBX_NONCE_CHLNG_TAG; + triplet[0].len = OBX_NONCE_SIZE; + triplet[0].p_array = nonce; + sprintf ((char *)nonce, "%016lu", GKI_get_tick_count()); + OBX_AddTriplet(p_pkt, OBX_HI_CHALLENGE, triplet, num_trip); + OBX_MD5 ((UINT8 *)(p_cb->p_auth), nonce, p_password, password_len); + } + + /* copy non-(target, conn_id, authentication) headers from p_saved_req. */ + status = obx_copy_and_rm_auth_hdrs(p_saved_req, p_pkt, hi); + OBX_TRACE_DEBUG4( "status:%d, save:%d, pkt:%d, off:%d", + status, p_saved_req->event, p_pkt->event, p_pkt->offset); + + if(status == OBX_SUCCESS) + { + /* get the final from the saved request */ + rsp_code = OBX_RSP_DEFAULT; + obx_access_rsp_code(p_saved_req, &rsp_code); + final = (rsp_code & OBX_FINAL) ? TRUE : FALSE; + OBX_TRACE_DEBUG1( "saved final:%d", final); + + /* call the associated API function to send the request again */ + switch(p_pkt->event) + { + case OBX_CONNECT_REQ_EVT: + status = OBX_ConnectReq((BD_ADDR_PTR)BT_BD_ANY, 0, 0, NULL, &p_cb->ll_cb.comm.handle, p_pkt); + break; + case OBX_PUT_REQ_EVT: + status = OBX_PutReq(handle, final, p_pkt); + break; + case OBX_GET_REQ_EVT: + status = OBX_GetReq(handle, final, p_pkt); + break; + case OBX_SETPATH_REQ_EVT: + /* get the flags from old request - if SetPath */ + flags = *((UINT8 *)(p_saved_req + 1) + p_saved_req->offset + 3); + status = OBX_SetPathReq(handle, flags, p_pkt); + break; + default: + /* it does not make sense to authenticate on Abort and Disconnect */ + OBX_TRACE_WARNING1( "Authenticate on bad request: %d", p_pkt->event); + status = OBX_NO_RESOURCES; + } /* switch (event) */ + } + } /* digest done */ + } /* challenge heaer exists */ + + if(status != OBX_SUCCESS) + { + GKI_freebuf(p_pkt); + } + + if(p_cb->wait_auth == FALSE) + { + GKI_freebuf(p_cb->p_auth); + p_cb->p_auth = NULL; + } + return status; +} + +#if (OBX_MD5_TEST_INCLUDED == TRUE) +/******************************************************************************* +** +** Function OBX_VerifyResponse +** +** Description This function is called by the client to verify the challenge +** response. +** +** Returns TRUE, if successful. +** FALSE, if authentication failed +** +*******************************************************************************/ +BOOLEAN OBX_VerifyResponse(UINT32 nonce_u32, UINT8 *p_password, UINT8 password_len, UINT8 *p_response) +{ + BOOLEAN status = FALSE; + UINT8 nonce[OBX_NONCE_SIZE + 1]; + UINT8 temp_digest[OBX_DIGEST_SIZE]; + + OBX_TRACE_API0( "OBX_VerifyResponse"); + if (p_password && password_len) + { + sprintf((char *)nonce, "%016lu", nonce_u32); + OBX_MD5 (temp_digest, nonce, p_password, password_len); + if (memcmp (temp_digest, p_response, OBX_DIGEST_SIZE) == 0) + status = TRUE; + } + return status; +} +#endif /* OBX_MD5_TEST_INCLUDED */ + + +#endif + + + diff --git a/stack/obx/obx_rfc.c b/stack/obx/obx_rfc.c new file mode 100644 index 0000000..b02fbef --- /dev/null +++ b/stack/obx/obx_rfc.c @@ -0,0 +1,613 @@ +/***************************************************************************** +** +** Name: obx_rfc.c +** +** File: OBEX interface to the RFCOMM module +** +** Copyright (c) 2003-2008, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include "wcassert.h" + +#include "bt_target.h" +#include "obx_int.h" + +#include "port_api.h" +#include "sdpdefs.h" +#include "btu.h" + +/******************************************************************************* +** Function obx_rfc_snd_evt +** Description Sends an rfcomm event to OBX through the BTU task. +*******************************************************************************/ +static void obx_rfc_snd_evt (tOBX_PORT_CB *p_pcb, UINT32 code) +{ + BT_HDR *p_msg; + tOBX_PORT_EVT *p_evt; + UINT16 event; + + if (!p_pcb) + return; + + p_msg = (BT_HDR*)GKI_getbuf(BT_HDR_SIZE + sizeof(tOBX_PORT_EVT)); + WC_ASSERT(p_msg); + + if (p_pcb->handle & OBX_CL_HANDLE_MASK) + event = BT_EVT_TO_OBX_CL_MSG; + else + event = BT_EVT_TO_OBX_SR_MSG; + + p_msg->event = event; + p_msg->len = sizeof(tOBX_PORT_EVT); + p_msg->offset = 0; + p_evt = (tOBX_PORT_EVT *)(p_msg + 1); + p_evt->code = code; + p_evt->p_pcb = p_pcb; + + GKI_send_msg(BTU_TASK, BTU_HCI_RCV_MBOX, p_msg); +} + +/******************************************************************************* +** Function obx_rfc_cback +** Description find the port control block and post an event to BTU task. +** NOTE: This callback does not handle connect up/down events. +** obx_rfc_mgmt_cback is used for these events. +*******************************************************************************/ +static void obx_rfc_cback (UINT32 code, UINT16 port_handle) +{ + tOBX_PORT_CB *p_pcb = obx_port_handle_2cb(port_handle); + + if (p_pcb) + { + obx_rfc_snd_evt (p_pcb, code); + } + else + { + OBX_TRACE_WARNING0("Can not find control block"); + } +} + +/******************************************************************************* +** Function obx_rfc_mgmt_cback +** Callback registered with the PORT entity's Management Callback so that OBX +** can be notified when the connection has come up or gone down. +********************************************************************************/ +void obx_rfc_mgmt_cback(UINT32 port_status, UINT16 port_handle) +{ + tOBX_PORT_CB *p_pcb = obx_port_handle_2cb(port_handle); + UINT32 code; + +#if (OBX_CLIENT_INCLUDED == TRUE) + if (!p_pcb && port_status != PORT_SUCCESS) + { + /* See if error called within RFCOMM_CreateConnection */ + if (obx_cb.p_temp_pcb) + { + p_pcb = obx_cb.p_temp_pcb; + obx_cb.p_temp_pcb = NULL; + } + } +#endif + + if (p_pcb) + { + code = (port_status == PORT_SUCCESS) ? PORT_EV_CONNECTED : PORT_EV_CONNECT_ERR; + obx_rfc_snd_evt (p_pcb, code); + } + else + { + OBX_TRACE_WARNING0("mgmt cback: Can not find control block"); + } +} + + +/******************************************************************************* +** Function obx_read_data +** Description This functions reads data from FRCOMM. Return a message if the +** whole packet is read. +** The following defines how BT_HDR is used in this function +** event: response or request event code +** len: the length read so far. +** offset: offset to the beginning of the actual data. +** layer_specific: left +*******************************************************************************/ +static BT_HDR * obx_read_data (tOBX_PORT_CB *p_pcb, tOBX_VERIFY_OPCODE p_verify_opcode) +{ + BT_HDR *p_ret = NULL; + UINT8 *p; + UINT16 ask_len; + UINT16 got_len; + int rc; + tOBX_RX_HDR *p_rxh; + tOBX_SR_SESS_CB *p_scb; + UINT8 opcode; + UINT16 pkt_len; + BOOLEAN failed = FALSE; + + OBX_TRACE_DEBUG1("obx_read_data port_handle:%d", p_pcb->port_handle ); + for (;;) + { + if (p_pcb->p_rxmsg == NULL) + { + p_pcb->p_rxmsg = OBX_HdrInit((tOBX_HANDLE)(p_pcb->handle|OBX_HANDLE_RX_MTU_MASK), + OBX_LRG_DATA_POOL_SIZE); + memset((p_pcb->p_rxmsg + 1), 0, sizeof(tOBX_RX_HDR)); + } + /* we use this header to keep the status of this packet (instead of in control block) */ + p_rxh = (tOBX_RX_HDR *)(p_pcb->p_rxmsg + 1); + + ask_len = 0; + if (p_rxh->code == 0) + { + if (p_pcb->p_rxmsg->len == 0) /* we need this if statement in case of "throw away" */ + ask_len = 1; + } + else if (p_pcb->p_rxmsg->len < (OBX_PKT_LEN_SIZE + 1) ) + { + /* if we do not know the packet len yet, read from port */ + ask_len = OBX_PKT_LEN_SIZE + 1 - p_pcb->p_rxmsg->len; + } + else + { + /* we already know the packet len. + * determine how many more bytes we need for this packet */ + ask_len = p_rxh->pkt_len - p_pcb->p_rxmsg->len; + } + + /* the position of next byte to read */ + p = (UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset + p_pcb->p_rxmsg->len; + + if (ask_len) + { + rc = PORT_ReadData( p_pcb->port_handle, (char*)p, ask_len, &got_len); + if (rc != PORT_SUCCESS) + { + OBX_TRACE_WARNING2("Error %d returned from PORT_Read_Data, len:%d", rc, got_len); + } + + OBX_TRACE_DEBUG2("ask_len: %d, got_len:%d", ask_len, got_len ); + if (got_len == 0) + { + /* If we tried to read but did not get anything, */ + /* there is nothing more to read at this time */ + break; + } + p_pcb->p_rxmsg->len += got_len; + p_pcb->p_rxmsg->layer_specific -= got_len; + } + + /* process the response/opcode, if not yet */ + if (p_rxh->code == 0 && p_pcb->p_rxmsg->len) + { + opcode = *((UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset); + if ( (p_verify_opcode)(opcode, p_rxh) == OBX_BAD_SM_EVT) + { + OBX_TRACE_WARNING1("bad opcode:0x%x - Disconnecting", opcode ); + /* received data with bad length. */ + + /*bad length disconnect */ + failed = TRUE; + break; + } + continue; + } + + /* process the packet len */ + if (p_rxh->pkt_len == 0 && p_pcb->p_rxmsg->len >= (OBX_PKT_LEN_SIZE + 1) ) + { + p = (UINT8 *)(p_pcb->p_rxmsg + 1) + p_pcb->p_rxmsg->offset + 1; + BE_STREAM_TO_UINT16(pkt_len, p); + + if ( (pkt_len > p_pcb->rx_mtu) || + (pkt_len < 3) || + (pkt_len == 4) ) + { + /* received data with bad length. */ + OBX_TRACE_WARNING2("Received bad packet len -Disconnecting: %d RX MTU: %x", + pkt_len, p_pcb->rx_mtu); + /*bad length disconnect */ + failed = TRUE; + break; + } + else + { + /* keep the packet len in the header */ + p_rxh->pkt_len = pkt_len; + } + continue; + } + + if (p_pcb->p_rxmsg->len == p_rxh->pkt_len) + { + /* received a whole packet */ + OBX_TRACE_DEBUG1("got a packet. opcode:0x%x", p_rxh->code ); + p_ret = p_pcb->p_rxmsg; + p_pcb->p_rxmsg = NULL; + break; + } + + } + + if (failed) + { + if (p_pcb->handle & OBX_CL_HANDLE_MASK) + { + obx_close_port(p_pcb->port_handle); + } + else + { + if ((p_scb = obx_sr_get_scb(p_pcb->handle)) != NULL) + obx_ssm_event(p_scb, OBX_PORT_CLOSE_SEVT, NULL); + + } + p_ret = NULL; + } + + return p_ret; +} + + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_cl_proc_evt +** Description This is called to process BT_EVT_TO_OBX_CL_MSG +** Process events from RFCOMM. Get the associated client control +** block. If this is a response packet, stop timer. Call +** obx_csm_event() with event OK_CFM, FAIL_CFM or CONT_CFM. +*******************************************************************************/ +void obx_cl_proc_evt(tOBX_PORT_EVT *p_evt) +{ + tOBX_PORT_CB *p_pcb = p_evt->p_pcb; + tOBX_CL_CB *p_cb = obx_cl_get_cb(p_pcb->handle); + BT_HDR *p_pkt; + + if (p_cb == NULL) + { + /* probably already close the port and deregistered from OBX */ + OBX_TRACE_ERROR1("Could not find control block for handle: 0x%x", p_pcb->handle); + return; + } + + if (p_evt->code & PORT_EV_CONNECT_ERR) + { + obx_csm_event(p_cb, OBX_PORT_CLOSE_CEVT, NULL); + return; + } /* PORT_EV_CONNECT_ERR */ + + if (p_evt->code & PORT_EV_TXEMPTY) + { + obx_csm_event(p_cb, OBX_TX_EMPTY_CEVT, NULL); + } /* PORT_EV_TXEMPTY */ + + if (p_evt->code & PORT_EV_RXCHAR) + { + while( (p_pkt = obx_read_data(p_pcb, obx_verify_response)) != NULL ) + { + if (GKI_queue_is_empty(&p_pcb->rx_q)) + { + if (p_pkt->event != OBX_BAD_SM_EVT) + { + obx_cl_proc_pkt (p_cb, p_pkt); + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + } + else if (p_pkt->event != OBX_BAD_SM_EVT) + { + GKI_enqueue (&p_pcb->rx_q, p_pkt); + if (p_pcb->rx_q.count > obx_cb.max_rx_qcount) + { + p_pcb->stopped = TRUE; + PORT_FlowControl(p_pcb->port_handle, FALSE); + } + } + } /* while received a packet */ + } /* PORT_EV_RXCHAR */ + + if (p_evt->code & PORT_EV_FC) + { + if (p_evt->code & PORT_EV_FCS) + { + OBX_TRACE_EVENT0("cl flow control event - FCS SET ----" ); + obx_csm_event(p_cb, OBX_FCS_SET_CEVT, NULL); + } + } /* PORT_EV_FC */ +} +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_build_dummy_rsp +** Description make up a dummy response if the app does not call response API +** yet and AbortRsp is called +*******************************************************************************/ +BT_HDR * obx_build_dummy_rsp(tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code) +{ + BT_HDR *p_pkt; + UINT8 *p; + UINT16 size = 3; + + p_pkt = OBX_HdrInit(p_scb->ll_cb.comm.handle, OBX_CMD_POOL_SIZE); + p = (UINT8 *)(p_pkt+1)+p_pkt->offset+p_pkt->len; + *p++ = (rsp_code|OBX_FINAL); + if (p_scb->conn_id) + { + size += 5; + UINT16_TO_BE_STREAM(p, size); + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_scb->conn_id); + } + else + { + UINT16_TO_BE_STREAM(p, size); + } + p_pkt->len = size; + p_pkt->event = OBX_PUT_RSP_EVT; /* or OBX_GET_RSP_EVT: for tracing purposes */ + return p_pkt; +} + +/******************************************************************************* +** Function obx_add_port +** Description check if this server has aother un-used port to open +** +** +** Returns void +*******************************************************************************/ +void obx_add_port(tOBX_HANDLE obx_handle) +{ + tOBX_SR_CB * p_cb = obx_sr_get_cb(obx_handle); + tOBX_SR_SESS_CB *p_scb, *p_scb0; + int xx; + tOBX_STATUS status = OBX_NO_RESOURCES; + BOOLEAN found; + + OBX_TRACE_DEBUG1("obx_add_port handle:0x%x", obx_handle ); + if (p_cb && p_cb->scn) + { + OBX_TRACE_DEBUG2("num_sess:%d scn:%d", p_cb->num_sess, p_cb->scn ); + p_scb0 = &obx_cb.sr_sess[p_cb->sess[0]-1]; + found = FALSE; + /* find an RFCOMM port that is not connected yet */ + for (xx=0; xx < p_cb->num_sess && p_cb->sess[xx]; xx++) + { + p_scb = &obx_cb.sr_sess[p_cb->sess[xx]-1]; + OBX_TRACE_DEBUG3("[%d] id:0x%x, state:%d", xx, p_scb->ll_cb.comm.id, p_scb->state ); + + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg + && p_scb->state == OBX_SS_NOT_CONNECTED) + { + found = TRUE; + break; + } + } + + if (!found) + { + for (xx=0; xx < p_cb->num_sess && p_cb->sess[xx]; xx++) + { + p_scb = &obx_cb.sr_sess[p_cb->sess[xx]-1]; + OBX_TRACE_DEBUG2("[%d] port_handle:%d", xx, p_scb->ll_cb.port.port_handle ); + if (!p_scb->ll_cb.comm.id) + { + status = obx_open_port(&p_scb->ll_cb.port, BT_BD_ANY, p_cb->scn); + if (status == OBX_SUCCESS) + { + p_scb->ll_cb.port.rx_mtu= p_scb0->ll_cb.port.rx_mtu; + p_scb->state = OBX_SS_NOT_CONNECTED; + } + break; + } + } + } + } +} + +/******************************************************************************* +** Function obx_sr_proc_evt +** Description This is called to process BT_EVT_TO_OBX_SR_MSG +** Process events from RFCOMM. Get the associated server control +** block. If this is a request packet, stop timer. Find the +** associated API event and save it in server control block +** (api_evt). Fill the event parameter (param). +** Call obx_ssm_event() with the associated events.If the associated +** control block is not found (maybe the target header does not +** match) or busy, compose a service unavailable response and call +** obx_rfc_snd_msg(). +*******************************************************************************/ +void obx_sr_proc_evt(tOBX_PORT_EVT *p_evt) +{ + tOBX_SR_SESS_CB *p_scb; + BT_HDR *p_pkt; + tOBX_RX_HDR *p_rxh; + tOBX_PORT_CB *p_pcb = p_evt->p_pcb; + + + OBX_TRACE_DEBUG2("obx_sr_proc_evt handle: 0x%x, port_handle:%d", p_evt->p_pcb->handle, p_evt->p_pcb->port_handle); + if (p_pcb->handle == 0 || p_pcb->p_send_fn != (tOBX_SEND_FN*)obx_rfc_snd_msg) + return; + + if ((p_scb = obx_sr_get_scb(p_pcb->handle)) == NULL) + { + /* probably already close the port and deregistered from OBX */ + OBX_TRACE_ERROR1("Could not find control block for handle: 0x%x", p_pcb->handle); + return; + } + + if (p_evt->code & PORT_EV_CONNECTED) + { + p_scb->ll_cb.port.tx_mtu = OBX_MIN_MTU; + obx_start_timer(&p_scb->ll_cb.comm); + /* Get the Bd_Addr */ + PORT_CheckConnection (p_scb->ll_cb.port.port_handle, + p_scb->param.conn.peer_addr, + NULL); + memcpy(p_scb->peer_addr, p_scb->param.conn.peer_addr, BD_ADDR_LEN); + } + + if (p_evt->code & PORT_EV_CONNECT_ERR) + { + obx_ssm_event(p_scb, OBX_PORT_CLOSE_SEVT, NULL); + return; + } /* PORT_EV_CONNECT_ERR */ + + if (p_evt->code & PORT_EV_RXCHAR) + { + while( (p_pkt = obx_read_data(p_pcb, obx_verify_request)) != NULL) + { + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + p_pkt->event = obx_sm_evt_to_api_evt[p_rxh->sm_evt]; + if (GKI_queue_is_empty(&p_pcb->rx_q)) + { + if (p_pkt->event != OBX_BAD_SM_EVT) + { + obx_sr_proc_pkt (p_scb, p_pkt); + } + else + { + OBX_TRACE_ERROR0("bad SM event" ); + } + } + else + { + GKI_enqueue (&p_pcb->rx_q, p_pkt); + if (p_pcb->rx_q.count > obx_cb.max_rx_qcount) + { + p_pcb->stopped = TRUE; + PORT_FlowControl(p_pcb->port_handle, FALSE); + } + } + } /* while a packet */ + } /* PORT_EV_RXCHAR */ + + /* The server does not need to handle this event + */ + if (p_evt->code & PORT_EV_TXEMPTY) + { + obx_ssm_event(p_scb, OBX_TX_EMPTY_SEVT, NULL); + } + + if (p_evt->code & PORT_EV_FC) + { + if (p_evt->code & PORT_EV_FCS) + { + OBX_TRACE_EVENT0("sr flow control event - FCS SET ----" ); + obx_ssm_event(p_scb, OBX_FCS_SET_SEVT, NULL); + } + } /* PORT_EV_FC */ +} +#endif /* OBX_SERVER_INCLUDED */ + +/******************************************************************************* +** Function obx_open_port +** Description Call RFCOMM_CreateConnection() to get port_handle. +** Call PORT_SetEventCallback() with given callback. +** Call PORT_SetEventMask() with given event mask. Return port handle. +** Returns port handle +*******************************************************************************/ +tOBX_STATUS obx_open_port(tOBX_PORT_CB *p_pcb, const BD_ADDR bd_addr, UINT8 scn) +{ + tOBX_STATUS status = OBX_SUCCESS; /* successful */ + UINT16 port_rc; + BOOLEAN is_server = (p_pcb->handle & OBX_CL_HANDLE_MASK)?FALSE:TRUE; + UINT16 max_mtu = OBX_MAX_MTU; + + OBX_TRACE_DEBUG2("obx_open_port rxmtu:%d, cbmtu:%d", p_pcb->rx_mtu, max_mtu ); + + /* clear buffers from previous connection */ + obx_free_buf ((tOBX_LL_CB*)p_pcb); + + /* make sure the MTU is in registered range */ + if (p_pcb->rx_mtu > max_mtu) + p_pcb->rx_mtu = max_mtu; + if (p_pcb->rx_mtu < OBX_MIN_MTU) + p_pcb->rx_mtu = OBX_MIN_MTU; + +#if (OBX_CLIENT_INCLUDED == TRUE) + /* There's a remote chance that an error can occur in L2CAP before the handle + * before the handle can be assigned (server side only). We will save the + * client control block while the handle is not known */ + if (!is_server) + { + obx_cb.p_temp_pcb = p_pcb; + } +#endif + + port_rc = RFCOMM_CreateConnection ( UUID_PROTOCOL_OBEX, scn, + is_server, (UINT16)(p_pcb->rx_mtu+1), (BD_ADDR_PTR)bd_addr, + &p_pcb->port_handle, obx_rfc_mgmt_cback); + + OBX_TRACE_DEBUG3("obx_open_port rxmtu:%d, port_handle:%d, port.handle:0x%x", + p_pcb->rx_mtu, p_pcb->port_handle, p_pcb->handle ); + +#if (OBX_CLIENT_INCLUDED == TRUE) + if (!is_server) + { + obx_cb.p_temp_pcb = NULL; + } +#endif + + if (port_rc == PORT_SUCCESS) + { + obx_cb.hdl_map[p_pcb->port_handle - 1] = p_pcb->handle; + PORT_SetEventCallback (p_pcb->port_handle, obx_rfc_cback); + PORT_SetEventMask (p_pcb->port_handle, OBX_PORT_EVENT_MASK); + p_pcb->p_send_fn = (tOBX_SEND_FN *)obx_rfc_snd_msg; + p_pcb->p_close_fn = obx_close_port; + } + else + { + status = OBX_NO_RESOURCES; + } + + return status; +} + + +/******************************************************************************* +** Function obx_close_port +** Description Clear the port event mask and callback. Close the port. +** Returns void +*******************************************************************************/ +void obx_close_port(UINT16 port_handle) +{ + RFCOMM_RemoveConnection(port_handle); +} + +/******************************************************************************* +** Function obx_rfc_snd_msg +** Description Call PORT_WriteData() to send an OBEX message to peer. If +** all data is sent, free the GKI buffer that holds +** the OBEX message. If only portion of data is +** sent, adjust the BT_HDR for PART state. +** Returns TRUE if all data is sent +*******************************************************************************/ +BOOLEAN obx_rfc_snd_msg(tOBX_PORT_CB *p_pcb) +{ + BOOLEAN status = FALSE; + UINT16 bytes_written = 0; + + obx_stop_timer(&p_pcb->tle); + PORT_WriteData(p_pcb->port_handle, ((char*)(p_pcb->p_txmsg + 1)) + p_pcb->p_txmsg->offset, + p_pcb->p_txmsg->len, &bytes_written); + + obx_start_timer ((tOBX_COMM_CB *)p_pcb); + + if (bytes_written == p_pcb->p_txmsg->len) + { + GKI_freebuf(p_pcb->p_txmsg); + p_pcb->p_txmsg = NULL; + status = TRUE; + } + else + { + /* packet not completely written to RFCOMM */ + p_pcb->p_txmsg->offset += bytes_written; + p_pcb->p_txmsg->len -= bytes_written; + } + + return status; +} diff --git a/stack/obx/obx_sact.c b/stack/obx/obx_sact.c new file mode 100644 index 0000000..1602752 --- /dev/null +++ b/stack/obx/obx_sact.c @@ -0,0 +1,1519 @@ +/***************************************************************************** +** +** Name: obx_sact.c +** +** File: OBEX Server State Machine Action Functions +** +** Copyright (c) 2003-2012, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include <bt_target.h> + +#include "btu.h" +#include "obx_int.h" +#include "btm_api.h" + +/******************************************************************************* +** Function obx_sa_snd_rsp +** Description Call p_send_fn() to send the OBEX message to the peer. +** Start timer. Return NULL state.If data is partially sent, set +** next_state in port control block. Return PART state. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_snd_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = p_scb->state; + UINT8 rsp_code = OBX_RSP_DEFAULT; + tOBX_SR_CB *p_cb; + BOOLEAN not_cong = TRUE; + + obx_access_rsp_code(p_pkt, &rsp_code); + p_scb->cur_op = OBX_REQ_ABORT; + + + p_scb->ll_cb.comm.p_txmsg = p_pkt; + rsp_code &= ~OBX_FINAL; + /* Get and Put operation may need to adjust the state*/ + if (rsp_code != OBX_RSP_CONTINUE ) + { + if (p_scb->state == OBX_SS_GET_TRANSACTION || + p_scb->state == OBX_SS_PUT_SRM || + p_scb->state == OBX_SS_GET_SRM || + p_scb->state == OBX_SS_PUT_TRANSACTION) + { + state = OBX_SS_CONNECTED; + /* the SRM bits can not be cleared here, if aborting */ + if ((p_scb->srm &OBX_SRM_ABORT) == 0) + p_scb->srm &= OBX_SRM_ENABLE; + } + } + + OBX_TRACE_DEBUG2("obx_sa_snd_rsp sess_st:%d, event:%d", p_scb->sess_st, p_pkt->event); + if ((p_scb->sess_st == OBX_SESS_ACTIVE) && (p_pkt->event != (OBX_SESSION_CFM_SEVT + 1))) + { + p_scb->ssn++; + } + + if (p_scb->ll_cb.comm.p_send_fn(&p_scb->ll_cb) == FALSE) + { + p_scb->next_state = state; + state = OBX_SS_PARTIAL_SENT; + } + else if (p_scb->state == OBX_SS_GET_SRM) + { + if (((p_scb->srmp & OBX_SRMP_WAIT) == 0) && (rsp_code == OBX_RSP_CONTINUE)) + { + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg) + { + OBX_TRACE_DEBUG1("obx_sa_snd_rsp cong:%d", p_scb->ll_cb.l2c.cong); + if (p_scb->ll_cb.l2c.cong) + { + not_cong = FALSE; + } + } + + /* do not need to wait + - fake a get request event, so the profile would issue another GET response */ + if (not_cong) + { + p_cb = obx_sr_get_cb(p_scb->handle); + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, NULL); + } + } + p_scb->srmp &= ~OBX_SRMP_WAIT; + } + else if (p_scb->sess_st == OBX_SESS_SUSPENDING) + { + p_scb->ssn++; + p_scb->param.sess.ssn = p_scb->ssn; + p_scb->param.sess.nssn = p_scb->ssn; + p_scb->sess_st = OBX_SESS_SUSPEND; + p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_scb->state; + state = OBX_SS_SESS_INDICATED; + p_scb->api_evt = OBX_SESSION_REQ_EVT; + } + + return state; +} + +/******************************************************************************* +** Function obx_sa_snd_part +** Description Call p_send_fn() to send the left-over OBEX message to the +** peer. Start timer. If all the data is sent, call obx_ssm_event() +** with STATE event to next_state in the port control block. +** If (p_saved), call obx_ssm_event() to process the saved request. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_snd_part(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + UINT8 rsp_code = OBX_RSP_DEFAULT; + tOBX_SR_CB *p_cb; + + obx_access_rsp_code(p_scb->ll_cb.comm.p_txmsg , &rsp_code); + if (p_scb->ll_cb.comm.p_send_fn(&p_scb->ll_cb) == TRUE) + { + obx_ssm_event(p_scb, OBX_STATE_SEVT, NULL); + if (p_scb->p_next_req) + { + p_pkt = p_scb->p_next_req; + obx_access_rsp_code(p_pkt , &rsp_code); + p_scb->p_next_req = NULL; + p_scb->api_evt = (tOBX_EVENT)p_pkt->event; + obx_ssm_event(p_scb, (tOBX_SR_EVENT)(p_pkt->event-1), p_pkt); + } + + OBX_TRACE_DEBUG3("obx_sa_snd_part state:%d, srm:0x%x, rsp_code:0x%x", p_scb->state, p_scb->srm, rsp_code); + if (p_scb->state == OBX_SS_GET_SRM) + { + rsp_code &= ~OBX_FINAL; + if (((p_scb->srm & OBX_SRM_WAIT) == 0) && (rsp_code == OBX_RSP_CONTINUE)) + { + /* do not need to wait + - fake a get request event, so the profile would issue another GET response */ + p_cb = obx_sr_get_cb(p_scb->handle); + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, NULL); + } + } + } + return state; +} + +/******************************************************************************* +** Function obx_sa_abort_rsp +** Description Send an abort response. +** If Put/Get response has not been sent yet, +** send it before the abort response. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_abort_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + BT_HDR *p_dummy; + UINT8 rsp_code = OBX_RSP_CONTINUE; + + if (p_scb->cur_op != OBX_REQ_ABORT && ((p_scb->srm & OBX_SRM_ENGAGE) == 0)) + { + /* if we have not respond to an op yet, send a dummy response */ + if (p_scb->cur_op == (rsp_code|OBX_REQ_PUT) ) + rsp_code = OBX_RSP_INTRNL_SRVR_ERR; + p_dummy = obx_build_dummy_rsp(p_scb, rsp_code); + obx_sa_snd_rsp(p_scb, p_dummy); + } + + /* clear the SRM bits; leave only the enabled bit */ + p_scb->srm &= OBX_SRM_ENABLE; + state = obx_sa_snd_rsp(p_scb, p_pkt); + return state; +} + +/******************************************************************************* +** Function obx_sa_op_rsp +** Description Send response for Put/Get when Abort request is already received +*******************************************************************************/ +tOBX_SR_STATE obx_sa_op_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + if (p_scb->cur_op != OBX_REQ_ABORT) + state = obx_sa_snd_rsp(p_scb, p_pkt); +#if (BT_USE_TRACES == TRUE) + else + OBX_TRACE_WARNING0("OBX is not waiting for a rsp API!!"); +#endif + return state; +} + +/******************************************************************************* +** Function obx_verify_target +** Description Verify that target header or connection ID is correct. +** Make sure that they do not both exist +*******************************************************************************/ +static UINT8 obx_verify_target(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + UINT16 len = 0; + UINT8 rsp_code = OBX_RSP_SERVICE_UNAVL; + UINT32 conn_id = 0; + UINT8 *p_target = NULL; + + OBX_Read4ByteHdr(p_pkt, OBX_HI_CONN_ID, &conn_id); +/* Coverity: +Event unchecked_value: Return value of "OBX_ReadTargetHdr" is not checked +Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[unchecked_value] False-positive: If target headser does not exist, + p_target would remain the default value/NULL and len would be set to 0. + There's no need to check the return value of OBX_ReadTargetHdr +*/ + OBX_ReadTargetHdr(p_pkt, &p_target, &len, 0); + + if (p_cb->target.len) + { + /* directed connection: make sure the connection ID matches */ + if ( conn_id && conn_id == p_scb->conn_id ) + rsp_code = OBX_RSP_OK; + + if (p_cb->target.len == OBX_DEFAULT_TARGET_LEN) + { + /* the user verify target (cases like BIP that has multiple targets) */ + rsp_code = OBX_RSP_OK; + } + else if ( len == p_cb->target.len && p_target && + memcmp(p_cb->target.target, p_target, p_cb->target.len) == 0) + { + rsp_code = OBX_RSP_OK; + } + } + else + { + /* no target - request to inbox, like OPP */ + if (conn_id == 0 && len == 0) + { + if (p_scb->ll_cb.comm.tx_mtu < OBX_MIN_MTU) + rsp_code = OBX_RSP_FORBIDDEN; + else + rsp_code = OBX_RSP_OK; + } + } + + /* target header and connection ID are not supposed to exist in the same packet*/ + if (conn_id != 0 && p_target != NULL) + rsp_code = OBX_RSP_BAD_REQUEST; + + OBX_TRACE_DEBUG3("obx_verify_target rsp: %x, id:%x, code:%x", + rsp_code, conn_id, ((tOBX_RX_HDR *)(p_pkt + 1))->code); + if (rsp_code != OBX_RSP_OK) + p_pkt->event = OBX_CONNECT_RSP_EVT; + return rsp_code; +} + +/******************************************************************************* +** Function obx_conn_rsp +** Description Called by OBX_ConnectRsp() and obx_sa_connect_ind() to compose +** a connect response packet. +*******************************************************************************/ +BT_HDR * obx_conn_rsp(tOBX_SR_CB *p_cb, tOBX_SR_SESS_CB *p_scb, UINT8 rsp_code, BT_HDR *p_pkt) +{ + UINT8 msg[OBX_HDR_OFFSET + OBX_MAX_CONN_HDR_EXTRA]; + UINT8 *p = msg; + + /* response packets always have the final bit set */ + *p++ = (rsp_code | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + *p++ = OBX_VERSION; + *p++ = OBX_CONN_FLAGS; + UINT16_TO_BE_STREAM(p, p_scb->ll_cb.comm.rx_mtu); + + /* add session sequence number, if session is active */ + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = (p_scb->ssn+1); + } + + if (p_scb->conn_id && rsp_code == OBX_RSP_OK) + { + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_scb->conn_id); + } + + p_pkt = obx_sr_prepend_msg(p_pkt, msg, (UINT16)(p - msg) ); + + /* If the target is registered to server and the WHO headers not in the packet + * add WHO header here */ + p_pkt->event = OBX_CONNECT_RSP_EVT; + if (p_cb->target.len && p_cb->target.len != OBX_DEFAULT_TARGET_LEN && + OBX_CheckHdr(p_pkt, OBX_HI_WHO) == NULL) + { + OBX_AddByteStrHdr(p_pkt, OBX_HI_WHO, p_cb->target.target, p_cb->target.len); + /* adjust the packet len */ + obx_adjust_packet_len(p_pkt); + } + return p_pkt; +} + +/******************************************************************************* +** Function obx_sa_wc_conn_ind +** Description Connect Req is received when waiting for the port to close +** Call callback with OBX_CLOSE_IND_EVT to clean up the profiles +** then call obx_sa_connect_ind to process the connect req. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_wc_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_EVT_PARAM param; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + + obx_stop_timer(&p_scb->ll_cb.comm.tle); + memset(¶m, 0, sizeof(tOBX_EVT_PARAM)); + if (p_cb && p_cb->p_cback) + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, NULL); + obx_sa_connect_ind(p_scb, p_pkt); + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_connect_ind +** Description Save peer MTU in the server control block.If the server does not +** register authentication, call callback with OBX_CONNECT_REQ_EVT +** and the MTU from the request message. Return NULL state. +** If authenticate, compose an unauthorized response, and call +** obx_sa_snd_rsp() to send it to the client. Return WAIT_AUTH state. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_connect_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + UINT8 rsp_code = OBX_RSP_SERVICE_UNAVL; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + UINT8 *p; + tOBX_EVT_PARAM param; /* The event parameter. */ + + /* clear the SRM bits; leave only the enabled bit */ + p_scb->srm &= OBX_SRM_ENABLE; + + OBX_TRACE_DEBUG0("obx_sa_connect_ind"); + p_scb->api_evt = OBX_NULL_EVT; + + /* verify that the connect request is OK */ + rsp_code = obx_verify_target(p_cb, p_scb, p_pkt); + if (rsp_code == OBX_RSP_OK) + { + if (p_cb->target.len && p_scb->conn_id == 0) + { + /* if Connection ID is used for this connection and none assigned yet, + * - assign one */ + p_scb->conn_id = obx_sr_get_next_conn_id(); + OBX_TRACE_DEBUG1(" **** obx_sr_get_next_conn_id (0x%08x)", p_scb->conn_id); + } + + /* tx_mtu is processed in obx_sr_proc_evt() */ + if (p_cb->p_auth) + { + /* If client challenge us first, and the server registers for authentication: + * remove the authentication headers and challenge it back */ + /* send unauthorize response */ + p_pkt = obx_unauthorize_rsp(p_cb, p_scb, p_pkt); + state = OBX_SS_WAIT_AUTH; + } + else + { + if (OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL) + { + /* If client challenge us first, and the server does not register for authentication: + * report the challenge */ + p_scb->p_saved_msg = obx_dup_pkt(p_pkt); + state = OBX_SS_AUTH_INDICATED; + p_scb->api_evt = OBX_PASSWORD_EVT; + } + else + { + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + p = &p_scb->sess_info[OBX_SESSION_INFO_ID_IDX]; + UINT32_TO_BE_STREAM(p, p_scb->conn_id); + param.sess.p_sess_info = p_scb->sess_info; + param.sess.sess_op = OBX_SESS_OP_CREATE; + param.sess.sess_st = p_scb->sess_st; + param.sess.nssn = p_scb->param.ssn; + param.sess.ssn = p_scb->param.ssn; + param.sess.obj_offset = 0; + p = &p_scb->sess_info[OBX_SESSION_INFO_MTU_IDX]; + UINT16_TO_BE_STREAM(p, p_scb->param.conn.mtu); + memcpy(param.sess.peer_addr, p_scb->peer_addr, BD_ADDR_LEN); + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_REQ_EVT, param, NULL); + } + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CONNECT_REQ_EVT, p_scb->param, p_pkt); + } + } + } + else + { + /* bad target or connection ID - reject the request */ + rsp_code |= OBX_FINAL; + obx_access_rsp_code(p_pkt, &rsp_code); + state = OBX_SS_NOT_CONNECTED; + } + + if (state != OBX_SS_NULL && state != OBX_SS_AUTH_INDICATED) + { + /* each port has its own credit. + * It's very unlikely that we can be flow controlled here */ + obx_sa_snd_rsp(p_scb, p_pkt); + } + + return state; +} + +/******************************************************************************* +** Function obx_sa_auth_ind +** Description Save peer MTU and the OBEX message in the server control block. +** Call callback function with OBX_PASSWORD_EVT. +** Note: This action function is only valid when MD5 is included +** Leave this as a stub function to avoid altering the state machine +*******************************************************************************/ +tOBX_SR_STATE obx_sa_auth_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + UINT8 rsp_code; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + + p_scb->api_evt = OBX_NULL_EVT; + rsp_code = obx_verify_target(p_cb, p_scb, p_pkt); + + if (rsp_code == OBX_RSP_OK) + { + /* tx_mtu is processed in obx_sr_proc_evt() */ + if (OBX_CheckHdr(p_pkt, OBX_HI_AUTH_RSP) == NULL) + { + /* we are expecting authentication response in this state. + * if none is received, reject the request */ + p_pkt = obx_unauthorize_rsp(p_cb, p_scb, p_pkt); + state = OBX_SS_NOT_CONNECTED; + } + else + { + /* the client sends authentication response. + * save a copy in the control block. Verify it when OBX_Password() is issued */ + p_scb->p_saved_msg = obx_dup_pkt(p_pkt); + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_PASSWORD_EVT, p_scb->param, p_pkt); + } + } + else + { + /* bad target or connection ID - reject the request */ + rsp_code |= OBX_FINAL; + obx_access_rsp_code(p_pkt, &rsp_code); + state = OBX_SS_NOT_CONNECTED; + } + + if (state != OBX_SS_NULL) + { + /* each port has its own credit. + * It's very unlikely that we can be flow controlled here */ + obx_sa_snd_rsp(p_scb, p_pkt); + } + + return state; +} + +/******************************************************************************* +** Function obx_sa_connect_rsp +** Description obx_sa_snd_rsp().If(p_saved), free the OBEX message.If OK +** response, return NULL state.If unauthorized response, return +** WAIT_AUTH state.If other fail response, return NOT_CONN state. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_connect_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + UINT8 rsp_code = OBX_RSP_DEFAULT; + tOBX_SR_STATE state = p_scb->state; + + obx_access_rsp_code(p_pkt, &rsp_code); + + if (p_scb->p_saved_msg) + { + GKI_freebuf(p_scb->p_saved_msg); + p_scb->p_saved_msg = NULL; + } + + if ( rsp_code == (OBX_RSP_UNAUTHORIZED | OBX_FINAL) && + OBX_CheckHdr(p_pkt, OBX_HI_CHALLENGE) != NULL) + { + state = OBX_SS_WAIT_AUTH; + } + else if (rsp_code != (OBX_RSP_OK | OBX_FINAL) ) + state = OBX_SS_NOT_CONNECTED; + + /* each port has its own credit. + * It's very unlikely that we can be flow controlled here */ + obx_sa_snd_rsp(p_scb, p_pkt); + + return state; +} + +/******************************************************************************* +** Function obx_sa_connection_error +** Description Stop timer. Reopen transport. Call callback function with +** OBX_CLOSE_IND_EVT. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_connection_error(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_EVT_PARAM param; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + tOBX_SR_CBACK *p_cback = NULL; + tOBX_SR_STATE save_state; + + OBX_TRACE_DEBUG4("obx_sa_connection_error tx_mtu: %d, sess_st:%d state:%d, prev_state:%d", + p_scb->ll_cb.comm.tx_mtu, p_scb->sess_st, p_scb->state, p_scb->prev_state); + + if (p_cb) + { + p_cback = p_cb->p_cback; + memset(¶m, 0, sizeof(tOBX_EVT_PARAM)); + } + + /* clear buffers from previous connection */ + obx_free_buf (&p_scb->ll_cb); + + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + /* The transport is interrupted while a reliable session is active: + * report a suspend event fot application to save the information in NV */ + save_state = p_scb->prev_state; + p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_scb->srm; + param.sess.p_sess_info = p_scb->sess_info; + param.sess.sess_op = OBX_SESS_OP_TRANSPORT; + param.sess.sess_st = p_scb->sess_st; + param.sess.nssn = p_scb->ssn; + param.sess.ssn = p_scb->ssn; + param.sess.obj_offset = 0; + param.sess.timeout = OBX_SESS_TIMEOUT_VALUE; + if (save_state == OBX_SS_PARTIAL_SENT) + save_state = p_scb->next_state; + + if ((p_scb->srm & OBX_SRM_ENGAGE) == 0) + { + /* SRM is not engaged. + * When the session is resume, client needs to send the request first, + * the save ssm state may need to be adjusted */ + if (save_state == OBX_SS_PUT_INDICATED) + { + save_state = OBX_SS_PUT_TRANSACTION; + } + else if (save_state == OBX_SS_GET_INDICATED) + { + save_state = OBX_SS_GET_TRANSACTION; + } + } + p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = save_state; + OBX_TRACE_DEBUG2("saved state:0x%x, srm:0x%x", save_state, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + memcpy(param.sess.peer_addr, p_scb->peer_addr, BD_ADDR_LEN); + p_scb->sess_st = OBX_SESS_NONE; + if (p_cback) + (*p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_INFO_EVT, param, NULL); + } + else if (p_scb->sess_st == OBX_SESS_CLOSE || p_scb->state == OBX_SS_NOT_CONNECTED) + p_scb->sess_st = OBX_SESS_NONE; + + if (p_scb->ll_cb.comm.tx_mtu != 0) + { + p_scb->ll_cb.comm.tx_mtu = 0; + obx_stop_timer(&p_scb->ll_cb.comm.tle); + p_scb->conn_id = 0; + if (p_cback) + (*p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, p_pkt); + } + + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg) + { + p_scb->ll_cb.comm.id = 0; /* mark this port unused. */ + p_scb->srm &= OBX_SRM_ENABLE; + obx_add_port (p_scb->handle); + } + + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_close_port +** Description Close transport. Start timer. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_close_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + if (p_pkt) + GKI_freebuf(p_pkt); + p_scb->ll_cb.comm.p_close_fn(p_scb->ll_cb.comm.id); + obx_sr_free_scb(p_scb); + obx_start_timer(&p_scb->ll_cb.comm); + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_clean_port +** Description Close transport and clean up api_evt if illegal obex message is +** received. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_clean_port(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + p_scb->api_evt = OBX_NULL_EVT; + return obx_sa_close_port(p_scb, p_pkt); +} +/******************************************************************************* +** Function obx_sa_state +** Description change state +*******************************************************************************/ +tOBX_SR_STATE obx_sa_state(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + return p_scb->next_state; +} + +/******************************************************************************* +** Function obx_sa_nc_to +** Description Timer expires in not_conn state +** if there is existing connections -> disconnect +*******************************************************************************/ +tOBX_SR_STATE obx_sa_nc_to(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + BD_ADDR bd_addr; + + obx_stop_timer(&p_scb->ll_cb.comm.tle); + /* if there is existing connections -> disconnect */ + if (OBX_GetPeerAddr(p_scb->ll_cb.comm.handle, bd_addr) != 0) + { + p_scb->ll_cb.comm.p_close_fn(p_scb->ll_cb.comm.id); + p_scb->conn_id = 0; + /* wait for the conn err event to re-open the port */ + } + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_save_req +** Description When a request received from peer in PART state, +** save the request for later processing +*******************************************************************************/ +tOBX_SR_STATE obx_sa_save_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + if (p_scb->p_next_req) + { + GKI_freebuf(p_scb->p_next_req); + } + p_scb->p_next_req = p_pkt; + p_scb->api_evt = OBX_NULL_EVT; + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_rej_req +** Description Send bad request response when request comes in bad state +*******************************************************************************/ +tOBX_SR_STATE obx_sa_rej_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + UINT8 msg[OBX_HDR_OFFSET]; + UINT8 *p = msg; + + OBX_TRACE_DEBUG0( "obx_sa_rej_req" ) ; + if (p_pkt) + GKI_freebuf(p_pkt); + + /* response packets always have the final bit set */ + *p++ = (OBX_RSP_SERVICE_UNAVL | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + /* add session sequence number, if session is active */ + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = (p_scb->ssn+1); + } + + /* add connection ID, if needed */ + if (p_scb->conn_id) + { + *p++ = OBX_HI_CONN_ID; + UINT32_TO_BE_STREAM(p, p_scb->conn_id); + } + + p_pkt = obx_sr_prepend_msg(NULL, msg, (UINT16)(p - msg) ); + p_pkt->event = OBX_PUT_RSP_EVT; /* any response */ + p_scb->api_evt = OBX_NULL_EVT; + + return obx_sa_snd_rsp(p_scb, p_pkt); +} + +/******************************************************************************* +** Function obx_sa_get_ind +** Description received a GET request from the client. Check if SRM is engaged. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_get_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + + if ((p_scb->srm & OBX_SRM_REQING) && (p_scb->srm & OBX_SRM_ENABLE)) + { + state = OBX_CS_GET_SRM; + } + + /* the GET request does not set the final bit. + * Save this request, send response automatically and do not report the event */ + if ((!p_scb->param.get.final) && ((p_scb->srmp & OBX_SRMP_NONF_EVT) == 0)) + { + p_scb->srmp |= OBX_SRMP_NONF; + if (p_scb->p_saved_msg) + GKI_freebuf(p_scb->p_saved_msg); + p_scb->p_saved_msg = p_pkt; + p_scb->api_evt = OBX_NULL_EVT; + OBX_GetRsp(p_scb->ll_cb.comm.handle, OBX_RSP_CONTINUE, NULL); + } + return state; +} + +/******************************************************************************* +** Function obx_merge_get_req +** Description merge the given 2 GET request packets and return the merged result +*******************************************************************************/ +BT_HDR * obx_merge_get_req(BT_HDR *p_pkt1, BT_HDR *p_pkt2) +{ + BT_HDR *p_ret = p_pkt1; + UINT16 size, need; + UINT8 *p1, *p2; + UINT8 *p, pre_size = OBX_GET_HDRS_OFFSET; + + /* skip the connection ID header */ + p = (UINT8 *)(p_pkt2 + 1) + p_pkt2->offset; + if (*p == OBX_HI_CONN_ID) + pre_size += 5; + + need = p_pkt1->len + p_pkt2->len; + if ((p_pkt2->len == pre_size) || (need >= OBX_LRG_DATA_POOL_SIZE)) + { + GKI_freebuf (p_pkt2); + return p_pkt1; + } + + /* get rid of the GET request header - opcode(1) + packet len(2) (and maybe connection ID) before merging */ + p_pkt2->len -= pre_size; + p_pkt2->offset += pre_size; + size = GKI_get_buf_size(p_pkt1); + + if (size < need) + { + /* the original p_pkt1 is too small. + * Allocate a bigger GKI buffer, p_ret, and copy p_pkt1 into p_ret */ + if (need < GKI_MAX_BUF_SIZE) + { + /* Use the largest general pool to allow challenge tags appendage */ + p_ret = (BT_HDR *)GKI_getbuf(GKI_MAX_BUF_SIZE); + } + else + { + p_ret = (BT_HDR *) GKI_getpoolbuf(OBX_LRG_DATA_POOL_ID); + } + memcpy (p_ret, p_pkt1, sizeof (BT_HDR)); + p_ret->offset = 0; + p1 = (UINT8 *)(p_ret + 1); + p2 = (UINT8 *)(p_pkt1 + 1) + p_pkt1->offset; + memcpy (p1, p2, p_pkt1->len); + GKI_freebuf (p_pkt1); + } + + /* adjust the actualy packet length to reflect the combined packet and copy p_pkt2 into p_ret */ + p1 = (UINT8 *)(p_ret + 1) + p_ret->offset + 1; + size = p_ret->len + p_pkt2->len; + UINT16_TO_BE_STREAM(p1, size); + p1 = (UINT8 *)(p_ret + 1) + p_ret->offset + p_ret->len; + p2 = (UINT8 *)(p_pkt2 + 1) + p_pkt2->offset; + p_ret->len = size; + memcpy (p1, p2, p_pkt2->len); + GKI_freebuf (p_pkt2); + + return p_ret; +} + +/******************************************************************************* +** Function obx_sa_get_req +** Description +*******************************************************************************/ +tOBX_SR_STATE obx_sa_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + tOBX_SR_CB *p_cb; + + OBX_TRACE_DEBUG2("obx_sa_get_req srmp:0x%x final:%d", p_scb->srmp, p_scb->param.get.final); + if (p_scb->srmp & OBX_SRMP_NONF) + { + /* the GET request does not set the final bit yet. + * merge this request, send response automatically and do not report the event */ + if (!p_scb->param.get.final) + { + p_scb->p_saved_msg = obx_merge_get_req(p_scb->p_saved_msg, p_pkt); + p_scb->api_evt = OBX_NULL_EVT; + OBX_GetRsp(p_scb->ll_cb.comm.handle, OBX_RSP_CONTINUE, NULL); + } + else + { + p_scb->srmp &= ~OBX_SRMP_NONF; + p_pkt = obx_merge_get_req(p_scb->p_saved_msg, p_pkt); + p_scb->p_saved_msg = NULL; + p_scb->api_evt = OBX_NULL_EVT; + p_cb = &obx_cb.server[p_scb->handle - 1]; + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, p_pkt); + memset(&p_scb->param, 0, sizeof (p_scb->param) ); + } + } + + return state; +} + +/******************************************************************************* +** Function obx_sa_make_sess_id +** Description compute the session id +*******************************************************************************/ +static tOBX_STATUS obx_sa_make_sess_id (tOBX_SR_SESS_CB *p_scb, UINT8 *p_sess_info, + tOBX_TRIPLET *p_triplet, UINT8 num_triplet) +{ + UINT8 data[10]; + UINT8 ind; + UINT8 *p; + + /* check the device address session parameter */ + ind = obx_read_triplet(p_triplet, num_triplet, OBX_TAG_SESS_PARAM_ADDR); + OBX_TRACE_DEBUG2("addr ind:%d, num:%d", ind, num_triplet); + if (ind == num_triplet || p_triplet[ind].len != BD_ADDR_LEN) + { + OBX_TRACE_ERROR0("No Device Addr parameter"); + return OBX_BAD_PARAMS; + } + + if (memcmp (p_scb->peer_addr, p_triplet[ind].p_array, BD_ADDR_LEN) != 0) + { + OBX_TRACE_ERROR0("Bad Device Addr parameter"); + return OBX_BAD_PARAMS; + } + + /* check the nonce session parameter */ + ind = obx_read_triplet(p_triplet, num_triplet, OBX_TAG_SESS_PARAM_NONCE); + OBX_TRACE_DEBUG2("nonce ind:%d, num:%d", ind, num_triplet); + if (ind == num_triplet || (p_triplet[ind].len < OBX_MIN_NONCE_SIZE) || (p_triplet[ind].len > OBX_NONCE_SIZE)) + { + OBX_TRACE_ERROR0("No Nonce parameter"); + return OBX_BAD_PARAMS; + } + p = data; + BTM_GetLocalDeviceAddr (p); + + /* compute the session ID */ + obx_session_id (p_sess_info, p_scb->peer_addr, p_triplet[ind].p_array, p_triplet[ind].len, + data, &p_scb->sess_info[OBX_SESSION_ID_SIZE], OBX_LOCAL_NONCE_SIZE); + + return OBX_SUCCESS; +} + +/******************************************************************************* +** Function obx_sa_session_ind +** Description process session request from client +** when the session request is received is not OBX_SS_NOT_CONNECTED +*******************************************************************************/ +tOBX_SR_STATE obx_sa_session_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_TRIPLET triplet[OBX_MAX_SESS_PARAM_TRIP]; + UINT8 num = OBX_MAX_SESS_PARAM_TRIP, ind; + UINT8 *p; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + UINT8 rsp_code = OBX_RSP_FORBIDDEN; + BOOLEAN now = FALSE; +#if (BT_USE_TRACES == TRUE) + tOBX_SESS_ST old_sess_st = p_scb->sess_st; +#endif + tOBX_EVENT old_api_evt; + UINT32 obj_offset = 0; + UINT8 ind_to; + UINT32 timeout = OBX_INFINITE_TIMEOUT; + tOBX_SR_STATE state = OBX_SS_NULL; + + OBX_TRACE_DEBUG0("obx_sa_session_ind"); + OBX_ReadTriplet(p_pkt, OBX_HI_SESSION_PARAM, triplet, &num); + if (p_cb->nonce == 0) + { + OBX_TRACE_ERROR0("reliable session is not supported by this server"); + /* do not report the session_req_evt */ + p_scb->api_evt = OBX_NULL_EVT; + obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, OBX_RSP_NOT_IMPLEMENTED, NULL); + return OBX_SS_NULL; + } + else if (num) + { + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP); + OBX_TRACE_DEBUG2("sess_op ind:%d, num:%d", ind, num); + if ((ind != num) && (triplet[ind].len == OBX_LEN_SESS_PARAM_SESS_OP)) + { + p = triplet[ind].p_array; + p_scb->param.sess.sess_op = *p; + OBX_TRACE_DEBUG1("sess_op :%d", *p); + switch (*p) + { + case OBX_SESS_OP_CREATE: + /* do not report the API event */ + p_scb->api_evt = OBX_NULL_EVT; + /* the session is already active; reject with Service Unavailable */ + rsp_code = OBX_RSP_SERVICE_UNAVL; + break; + + case OBX_SESS_OP_CLOSE: + /* verify that the session ID matches an existing one. Otherwise, FORBIDDEN */ + if (p_scb->sess_st != OBX_SESS_NONE) + { + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_ID); + if (ind == num || triplet[ind].len != OBX_SESSION_ID_SIZE) + break; + if (memcmp (p_scb->sess_info, triplet[ind].p_array, OBX_SESSION_ID_SIZE) != 0) + { + /* bad session id */ + break; + } + /* must be closing a good session 0 send the response now */ + now = TRUE; + rsp_code = OBX_RSP_OK; + p_scb->sess_st = OBX_SESS_CLOSE; + } + break; + + case OBX_SESS_OP_SUSPEND: + /* verify that a session is active. Otherwise, FORBIDDEN */ + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + rsp_code = OBX_RSP_OK; + p_scb->sess_st = OBX_SESS_SUSPEND; + p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] = p_scb->prev_state; + p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] = p_scb->srm; + /* save the session state in a GKI buffer on OBX_SessionRsp */ + if (p_scb->prev_state == OBX_SS_PUT_INDICATED || p_scb->prev_state == OBX_SS_GET_INDICATED) + { + /* out of sequence suspend: + * report the suspend event when the PutRsp or GetRsp is called + * this would allow server to resume the session in the right state */ + p_scb->api_evt = OBX_NULL_EVT; + p_scb->sess_st = OBX_SESS_SUSPENDING; + state = p_scb->prev_state; + } + } + break; + + case OBX_SESS_OP_RESUME: + rsp_code = OBX_RSP_SERVICE_UNAVL; + /* do not report the API event */ + p_scb->api_evt = OBX_NULL_EVT; + break; + + case OBX_SESS_OP_SET_TIME: + /* respond SET_TIME right away */ + now = TRUE; + + if (p_scb->sess_st == OBX_SESS_NONE) + { + rsp_code = OBX_RSP_FORBIDDEN; + } + break; + } + } + } + OBX_TRACE_DEBUG6("obx_sa_session_ind tx_mtu: %d, sess_st:%d->%d, rsp_code:0x%x, now:%d pstate:%d", + p_scb->ll_cb.comm.tx_mtu, old_sess_st, p_scb->sess_st, rsp_code, now, p_scb->prev_state); + + if (rsp_code == OBX_RSP_OK) + { + obx_read_timeout (triplet, num, &timeout, &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]); + } + + if ((rsp_code != OBX_RSP_OK) || now) + { + /* hold the original api_evt temporarily, so it's not reported at this obx_ssm_event */ + old_api_evt = p_scb->api_evt; + p_scb->api_evt = OBX_NULL_EVT; + /* send the response now */ + obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, rsp_code, NULL); + /* restore the api event */ + p_scb->api_evt = old_api_evt; + } + else + { + ind_to = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP); + if ((ind_to != num) && (triplet[ind_to].len == OBX_TIMEOUT_SIZE)) + { + p = triplet[ind_to].p_array; + BE_STREAM_TO_UINT32(timeout, p); + } + p_scb->param.sess.p_sess_info = p_scb->sess_info; + p_scb->param.sess.sess_st = p_scb->sess_st; + p_scb->param.sess.ssn = p_scb->ssn; + p_scb->param.sess.obj_offset = obj_offset; + p_scb->param.sess.timeout = timeout; + memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN); + + } + return state; +} + +/******************************************************************************* +** Function obx_sa_sess_conn_ind +** Description process session request from client +** when the session request is received in OBX_SS_NOT_CONNECTED +*******************************************************************************/ +tOBX_SR_STATE obx_sa_sess_conn_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + UINT8 sess_info[OBX_SESSION_INFO_SIZE]; /* session id + local nonce */ + tOBX_TRIPLET triplet[OBX_MAX_SESS_PARAM_TRIP]; + UINT8 num = OBX_MAX_SESS_PARAM_TRIP, ind; + UINT8 *p; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + UINT8 rsp_code = OBX_RSP_FORBIDDEN; + BOOLEAN now = FALSE; +#if (BT_USE_TRACES == TRUE) + tOBX_SESS_ST old_sess_st = p_scb->sess_st; +#endif + tOBX_SESS_OP sess_op = OBX_SESS_OP_CREATE; + tOBX_EVENT old_api_evt; + UINT32 obj_offset = 0;/* to report in evt param */ + UINT8 op_ssn; + UINT8 num_trip = 0; + BT_HDR *p_rsp = NULL; + UINT8 data[12]; + UINT8 ind_to; + UINT32 timeout = OBX_INFINITE_TIMEOUT; + UINT32 offset = 0; /* if non-0, add to triplet on resume */ + tOBX_SPND_CB *p_spndcb; + tOBX_EVT_PARAM param; /* The event parameter. */ + + OBX_TRACE_DEBUG0("obx_sa_sess_conn_ind"); + OBX_ReadTriplet(p_pkt, OBX_HI_SESSION_PARAM, triplet, &num); + if (p_cb->nonce == 0) + { + OBX_TRACE_ERROR0("reliable session is not supported by this server"); + /* do not report the session_req_evt */ + p_scb->api_evt = OBX_NULL_EVT; + obx_prepend_rsp_msg(p_scb->handle, OBX_SESSION_CFM_SEVT, OBX_RSP_NOT_IMPLEMENTED, NULL); + return OBX_SS_NULL; + } + else if (num) + { + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP); + OBX_TRACE_DEBUG2("sess_op ind:%d, num:%d", ind, num); + if ((ind != num) && (triplet[ind].len == OBX_LEN_SESS_PARAM_SESS_OP)) + { + p = triplet[ind].p_array; + p_scb->param.sess.sess_op = sess_op = *p; + OBX_TRACE_DEBUG1("sess_op :%d", *p); + switch (*p) + { + case OBX_SESS_OP_CREATE: + /* do not report the API event */ + p_scb->api_evt = OBX_NULL_EVT; + + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + /* the session is already active; reject with OBX_RSP_FORBIDDEN */ + break; + } + + /* check if we still have room for one more session */ + if (obx_find_suspended_session (p_scb, NULL, 0) == NULL) + { + rsp_code = OBX_RSP_DATABASE_FULL; + break; + } + + p = &p_scb->sess_info[OBX_SESSION_INFO_NONCE_IDX]; + UINT32_TO_BE_STREAM(p, p_cb->nonce); + p_cb->nonce++; + /* make sure it's not 0 (which means reliable session is disabled) */ + if (!p_cb->nonce) + p_cb->nonce++; + + if (obx_sa_make_sess_id (p_scb, p_scb->sess_info, triplet, num) == OBX_SUCCESS) + { + rsp_code = OBX_RSP_OK; + p_scb->ssn = 0; + p_scb->sess_st = OBX_SESS_CREATE; + } + break; + + case OBX_SESS_OP_RESUME: + /* verify that a previously interrupted session exists with the same session parameters. + Otherwise, OBX_RSP_SERVICE_UNAVL. + */ + p_spndcb = obx_find_suspended_session (p_scb, triplet, num); + if (p_spndcb) + { + op_ssn = p_spndcb->ssn; + memcpy (p_scb->sess_info, p_spndcb->sess_info, OBX_SESSION_INFO_SIZE); + if (obx_sa_make_sess_id (p_scb, sess_info, triplet, num) == OBX_SUCCESS && + memcmp (sess_info, p_scb->sess_info, OBX_SESSION_ID_SIZE) == 0) + { + /* clear the suspend cb info */ + p_spndcb->state = OBX_SS_NULL; + if (p_spndcb->stle.param) + { + btu_stop_timer (&p_spndcb->stle); + p_spndcb->stle.param = 0; + } + rsp_code = OBX_RSP_OK; + p_scb->sess_st = OBX_SESS_RESUME; + p_scb->srmp |= OBX_SRMP_SESS_FST; + ind = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_NSEQNUM); + if (ind != num) + { + /* ssn exists - must be immediate suspend */ + p = triplet[ind].p_array; + op_ssn = *p; + obj_offset = obx_read_obj_offset(triplet, num); + } + } + obxu_dump_hex (p_scb->sess_info, "sess info", OBX_SESSION_INFO_SIZE); + OBX_TRACE_DEBUG4("p_spndcb->offset: 0x%x srm:x%x op_ssn %d ssn %d", p_spndcb->offset, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX],op_ssn,p_scb->ssn); + + if (p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX] & OBX_SRM_ENGAGE) + { + /* + If offset in the request is smaller which only happens when it is a get operation, then client's offset and ssn matters. + So server's offset and ssn should be set to the client's ones. + If offset in the request is greater which only happens when it is a put operation, then server's offset and ssn matters. + So server keeps its offset(do nothing) and ssn and send it back to client in the resume reponse. + If offset are equal, either side's offset & ssn is fine, we choose to use the one in the reqeust + */ + + offset = p_spndcb->offset; + if (obj_offset && (obj_offset <= offset)) + { + offset = obj_offset; + p_scb->ssn = op_ssn; + /* Adjust ssn in the next continue to be the same as nssn in the resume request */ + if (p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] ==OBX_CS_GET_SRM) + p_scb->ssn--; + + + } + + } + /* SRM is not enabled */ + else + { + p_scb->ssn = op_ssn; + } + + OBX_TRACE_DEBUG4("offset: 0x%x ssn:%d obj_offset:0x%x srm:0x%x", offset, p_scb->ssn, obj_offset, p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX]); + } + + + if (rsp_code != OBX_RSP_OK) + { + rsp_code = OBX_RSP_SERVICE_UNAVL; + } + /* do not report the API event for RESUME. + * if OBX_RSP_OK, the event is reported in this function. + * p_rsp is used to call obx_ssm_event, and can not be reported to cback */ + p_scb->api_evt = OBX_NULL_EVT; + break; + } + } + } + else + { + /* do not have session parameters - bad req do not report the API event */ + p_scb->api_evt = OBX_NULL_EVT; + rsp_code = OBX_RSP_BAD_REQUEST; + } + OBX_TRACE_DEBUG5("obx_sa_sess_conn_ind tx_mtu: %d, sess_st:%d->%d, rsp_code:0x%x, now:%d", + p_scb->ll_cb.comm.tx_mtu, old_sess_st, p_scb->sess_st, rsp_code, now); + + if (rsp_code == OBX_RSP_OK) + { + /* send the session response now. + * do not report OBX_SESSION_REQ_EVT until connect indication, + * so connection id can be reported at the same time in sess_info */ + if ( (p_rsp = OBX_HdrInit(p_scb->handle, OBX_MIN_MTU))== NULL) + { + rsp_code = OBX_RSP_INTRNL_SRVR_ERR; + } + else + { + obx_read_timeout (triplet, num, &timeout, &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]); + + p = (UINT8 *) (p_rsp + 1) + p_rsp->offset; + /* response packet always has the final bit set */ + *p++ = (OBX_RSP_OK | OBX_FINAL); + p_rsp->len = 3; + p = data; + + /* add address */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_ADDR; + triplet[num_trip].len = BD_ADDR_LEN; + triplet[num_trip].p_array = p; + BTM_GetLocalDeviceAddr (p); + p += BD_ADDR_LEN; + num_trip++; + + /* add nonce 4 - 16 bytes */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NONCE; + triplet[num_trip].len = OBX_LOCAL_NONCE_SIZE; + triplet[num_trip].p_array = &p_scb->sess_info[OBX_SESSION_INFO_NONCE_IDX]; + num_trip++; + + /* add session id */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_SESS_ID; + triplet[num_trip].len = OBX_SESSION_ID_SIZE; + triplet[num_trip].p_array = p_scb->sess_info; + num_trip++; + + if (sess_op == OBX_SESS_OP_RESUME) + { + /* add session id */ + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_NSEQNUM; + triplet[num_trip].len = 1; + triplet[num_trip].p_array = p; + /* Adjust ssn in the resume response to be the same as nssn in the resume request */ + if (p_scb->sess_info[OBX_SESSION_INFO_ST_IDX] == OBX_CS_GET_SRM) + *p++ = p_scb->ssn+1; + else + *p++ = p_scb->ssn; + num_trip++; + if (offset) + { + triplet[num_trip].tag = OBX_TAG_SESS_PARAM_OBJ_OFF; + triplet[num_trip].len = OBX_LEN_SESS_PARAM_OBJ_OFF; + triplet[num_trip].p_array = p; + UINT32_TO_BE_STREAM(p, offset); + num_trip++; + obj_offset = offset; + } + } + + /* add timeout */ + if (timeout != OBX_INFINITE_TIMEOUT && obx_cb.sess_tout_val != OBX_INFINITE_TIMEOUT && (obx_cb.sess_tout_val > timeout)) + { + timeout = obx_cb.sess_tout_val; + triplet[num_trip].p_array = p; + num_trip += obx_add_timeout (&triplet[num_trip], obx_cb.sess_tout_val, &p_scb->param.sess); + p = &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]; + UINT32_TO_BE_STREAM(p, timeout); + } + OBX_AddTriplet(p_rsp, OBX_HI_SESSION_PARAM, triplet, num_trip); + + /* adjust the packet len */ + p = (UINT8 *) (p_rsp + 1) + p_rsp->offset + 1; + UINT16_TO_BE_STREAM(p, p_rsp->len); + p_rsp->event = OBX_SESSION_CFM_SEVT + 1; + p_scb->sess_st = OBX_SESS_ACTIVE; + p_scb->param.sess.p_sess_info = p_scb->sess_info; + p_scb->param.sess.sess_st = p_scb->sess_st; + p_scb->param.sess.ssn = p_scb->ssn; + p_scb->param.sess.nssn = p_scb->ssn; + p_scb->param.sess.obj_offset = obj_offset; + p_scb->param.sess.timeout = timeout; + memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN); + obx_ssm_event(p_scb, OBX_SESSION_CFM_SEVT, p_rsp); + + if (sess_op == OBX_SESS_OP_RESUME) + { + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_SESSION_REQ_EVT, p_scb->param, NULL); + memset(&p_scb->param, 0, sizeof (p_scb->param) ); + param.conn.ssn = p_scb->ssn; + memcpy (param.conn.peer_addr, p_scb->peer_addr, BD_ADDR_LEN); + p = &p_scb->sess_info[OBX_SESSION_INFO_MTU_IDX]; + BE_STREAM_TO_UINT16(param.conn.mtu, p); + p_scb->ll_cb.comm.tx_mtu = param.conn.mtu; + param.conn.handle = p_scb->ll_cb.comm.handle; + OBX_TRACE_DEBUG1("RESUME tx_mtu: %d", p_scb->ll_cb.comm.tx_mtu); + /* report OBX_CONNECT_REQ_EVT to let the client know the MTU */ + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CONNECT_REQ_EVT, param, NULL); + } + } + } + + if ((rsp_code != OBX_RSP_OK) || now) + { + /* hold the original api_evt temporarily, so it's not reported at this obx_ssm_event */ + old_api_evt = p_scb->api_evt; + p_scb->api_evt = OBX_NULL_EVT; + /* send the response now */ + obx_prepend_rsp_msg(p_scb->handle, OBX_DISCNT_CFM_SEVT, rsp_code, NULL); + /* restore the api event */ + p_scb->api_evt = old_api_evt; + } + else + { + ind_to = obx_read_triplet(triplet, num, OBX_TAG_SESS_PARAM_SESS_OP); + if ((ind_to != num) && (triplet[ind_to].len == OBX_TIMEOUT_SIZE)) + { + p = triplet[ind_to].p_array; + BE_STREAM_TO_UINT32(timeout, p); + } + p_scb->param.sess.p_sess_info = p_scb->sess_info; + p_scb->param.sess.sess_st = p_scb->sess_st; + p_scb->param.sess.ssn = p_scb->ssn; + p_scb->param.sess.nssn = p_scb->ssn; + p_scb->param.sess.obj_offset = obj_offset; + p_scb->param.sess.timeout = timeout; + memcpy(p_scb->param.sess.peer_addr , p_scb->peer_addr, BD_ADDR_LEN); + + } + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_wc_sess_ind +** Description process session request from client +** when the session request is received in OBX_SS_WAIT_CLOSE +*******************************************************************************/ +tOBX_SR_STATE obx_sa_wc_sess_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_EVT_PARAM param; + tOBX_SR_CB *p_cb = obx_sr_get_cb(p_scb->handle); + + OBX_TRACE_DEBUG1("obx_sa_wc_sess_ind sess_st:%d", p_scb->sess_st); + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + /* processing CloseSession */ + obx_sa_session_ind(p_scb, p_pkt); + } + else + { + /* probably CreateSession */ + obx_sa_sess_conn_ind(p_scb, p_pkt); + OBX_TRACE_DEBUG1("obx_sa_wc_sess_ind (after obx_sa_sess_conn_ind) sess_st:%d", p_scb->sess_st); + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + /* after session command and still is Active + * a session must have been created during Wait_close state. + * need to report a OBX_CLOSE_IND_EVT to clean up the profiles */ + memset(¶m, 0, sizeof(tOBX_EVT_PARAM)); + if (p_cb && p_cb->p_cback) + (*p_cb->p_cback)(p_scb->ll_cb.comm.handle, OBX_CLOSE_IND_EVT, param, NULL); + } + } + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_session_rsp +** Description process Session response API. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_session_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE new_state = OBX_SS_NULL; + UINT8 *p; + tOBX_L2C_CB *p_lcb; + tOBX_L2C_EVT_PARAM evt_param; + + OBX_TRACE_DEBUG1("obx_sa_session_rsp pstate:%d", p_scb->prev_state); + new_state = obx_sa_snd_rsp(p_scb, p_pkt); + OBX_TRACE_DEBUG2("sess_st: %d op:%d", p_scb->sess_st, p_scb->param.sess.sess_op); + if (p_scb->sess_st == OBX_SESS_ACTIVE && p_scb->param.sess.sess_op == OBX_SESS_OP_RESUME) + { + p_scb->srm = p_scb->sess_info[OBX_SESSION_INFO_SRM_IDX]; + new_state = p_scb->sess_info[OBX_SESSION_INFO_ST_IDX]; + p = &p_scb->sess_info[OBX_SESSION_INFO_ID_IDX]; + BE_STREAM_TO_UINT32(p_scb->conn_id, p); + OBX_TRACE_DEBUG3("new_state; %d Connection ID: 0x%x, srm:0x%x", new_state, p_scb->conn_id, p_scb->srm); + if ((p_scb->srm & OBX_SRM_ENGAGE) && (new_state == OBX_SS_GET_SRM)) + { + p_lcb = &p_scb->ll_cb.l2c; + evt_param.any = 0; + obx_l2c_snd_evt (p_lcb, evt_param, OBX_L2C_EVT_RESUME); + } + /* report OBX_CONNECT_REQ_EVT in obx_sa_sess_conn_ind() + if (new_state == OBX_SS_CONNECTED) + { + } */ + } + else if (p_scb->sess_st == OBX_SESS_CLOSE) + { + new_state = OBX_SS_WAIT_CLOSE; + } + + return new_state; +} + +/******************************************************************************* +** Function obx_sa_put_ind +** Description received a PUT request from the client. Check if SRM is engaged. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_put_ind(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + if ((p_scb->srm & OBX_SRM_REQING) && (p_scb->srm & OBX_SRM_ENABLE)) + { + state = OBX_CS_PUT_SRM; + } + return state; +} + +/******************************************************************************* +** Function obx_sa_srm_put_req +** Description received a PUT request from the client when SRM is engaged. +*******************************************************************************/ +tOBX_SR_STATE obx_sa_srm_put_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + if (!p_scb->param.put.final) + p_scb->srm |= OBX_SRM_WAIT_UL; + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_srm_put_rsp +** Description process PUT response API function. +** report PUT request event, if any is queued +*******************************************************************************/ +tOBX_SR_STATE obx_sa_srm_put_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + tOBX_COMM_CB *p_comm = &p_scb->ll_cb.comm; + UINT8 rsp_code = OBX_RSP_DEFAULT; + BOOLEAN ret = TRUE; + + obx_access_rsp_code(p_pkt, &rsp_code); + rsp_code &= ~OBX_FINAL; + if (rsp_code != OBX_RSP_CONTINUE ) + { + p_scb->srm |= OBX_SRM_NEXT; + if (rsp_code != OBX_RSP_OK) + p_scb->srm |= OBX_SRM_ABORT; + } + + p_scb->srm &= ~OBX_SRM_WAIT_UL; + OBX_TRACE_DEBUG2("obx_sa_srm_put_rsp srm:0x%x rsp_code:0x%x", p_scb->srm, rsp_code); + if (p_scb->srm & OBX_SRM_NEXT) + { + p_scb->srm &= ~OBX_SRM_NEXT; + state = obx_sa_snd_rsp (p_scb, p_pkt); + } + else + { + if (p_scb->sess_st == OBX_SESS_ACTIVE) + { + p_scb->ssn++; + } + obx_start_timer(&p_scb->ll_cb.comm); + if (p_pkt) + GKI_freebuf(p_pkt); + } + OBX_TRACE_DEBUG1("obx_sa_srm_put_rsp srm:0x%x", p_scb->srm); + + while (ret && (p_pkt = (BT_HDR *)GKI_dequeue (&p_comm->rx_q)) != NULL) + { + if (state != OBX_SS_NULL) + { + p_scb->state = state; + state = OBX_SS_NULL; + } + ret = obx_sr_proc_pkt (p_scb, p_pkt); + if ((p_scb->srm & OBX_SRM_ABORT) == 0) + ret = FALSE; + obx_flow_control(p_comm); + OBX_TRACE_DEBUG3("obx_sa_srm_put_rsp rx_q.count: %d srm:0x%x, ret:%d", p_comm->rx_q.count, p_scb->srm, ret ); + } + + return state; +} + +/******************************************************************************* +** Function obx_sa_srm_get_fcs +** Description Process L2CAP congestion event +*******************************************************************************/ +tOBX_SR_STATE obx_sa_srm_get_fcs(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + BOOLEAN not_cong = TRUE; + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_l2c_snd_msg) + { + OBX_TRACE_DEBUG1("obx_sa_srm_get_fcs cong:%d", p_scb->ll_cb.l2c.cong); + if (p_scb->ll_cb.l2c.cong) + not_cong = FALSE; + } + if (not_cong) + p_scb->api_evt = OBX_GET_REQ_EVT; + return OBX_SS_NULL; +} + +/******************************************************************************* +** Function obx_sa_srm_get_rsp +** Description send GET response to client +*******************************************************************************/ +tOBX_SR_STATE obx_sa_srm_get_rsp(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state; + OBX_TRACE_DEBUG1("obx_sa_srm_get_rsp srm:0x%x", p_scb->srm); + state = obx_sa_snd_rsp(p_scb, p_pkt); + return state; +} + + + +/******************************************************************************* +** Function obx_sa_srm_get_req +** Description process GET request from client +*******************************************************************************/ +tOBX_SR_STATE obx_sa_srm_get_req(tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_SR_STATE state = OBX_SS_NULL; + tOBX_SR_CB *p_cb; + + OBX_TRACE_DEBUG3("obx_sa_srm_get_req srm:0x%x srmp:0x%x final:%d", p_scb->srm, p_scb->srmp, p_scb->param.get.final); + if (p_scb->srmp & OBX_SRMP_NONF) + { + /* the GET request does not set the final bit yet. + * merge this request, send response automatically and do not report the event */ + if (!p_scb->param.get.final) + { + p_scb->p_saved_msg = obx_merge_get_req(p_scb->p_saved_msg, p_pkt); + p_scb->api_evt = OBX_NULL_EVT; + } + else + { + p_scb->srmp &= ~OBX_SRMP_NONF; + p_pkt = obx_merge_get_req(p_scb->p_saved_msg, p_pkt); + p_scb->p_saved_msg = NULL; + p_scb->api_evt = OBX_NULL_EVT; + p_cb = &obx_cb.server[p_scb->handle - 1]; + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, OBX_GET_REQ_EVT, p_scb->param, p_pkt); + memset(&p_scb->param, 0, sizeof (p_scb->param) ); + } + } + + return state; +} + diff --git a/stack/obx/obx_sapi.c b/stack/obx/obx_sapi.c new file mode 100644 index 0000000..7815947 --- /dev/null +++ b/stack/obx/obx_sapi.c @@ -0,0 +1,591 @@ +/***************************************************************************** +** +** Name: obx_sapi.c +** +** File: OBEX Server Application Programming Interface functions +** +** Copyright (c) 2003-2009, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "bt_target.h" + +#if defined(OBX_INCLUDED) && (OBX_INCLUDED == TRUE) + +#include "wcassert.h" +#include "btu.h" +#include "port_api.h" +#include "obx_int.h" +#include "l2c_api.h" +#include "btm_api.h" + +/******************************************************************************* +** +** Function OBX_StartServer +** +** Description This function is to register a server entity to OBEX. +** +** Returns OBX_SUCCESS, if successful. +** OBX_NO_RESOURCES, if OBX does not have resources. +** +*******************************************************************************/ +tOBX_STATUS OBX_StartServer(tOBX_StartParams *p_params, tOBX_HANDLE *p_handle) +{ + tOBX_SR_CB *p_cb = NULL; + tOBX_STATUS status = OBX_NO_RESOURCES; +// btla-specific ++ + tOBX_SR_SESS_CB *p_scb = NULL; +// btla-specific -- + tOBX_HANDLE obx_handle; + UINT8 size; + + WC_ASSERT(p_params); + WC_ASSERT(p_params->p_cback); + WC_ASSERT(p_handle); + + if (p_params->max_sessions > OBX_MAX_SR_SESSION) + { + OBX_TRACE_ERROR2("OBX_StartServer bad max_sessions:%d (1-%d)", + p_params->max_sessions, OBX_MAX_SR_SESSION); + return OBX_BAD_PARAMS; + } + + if (p_params->scn == 0 && L2C_INVALID_PSM(p_params->psm)) + { + OBX_TRACE_ERROR2("OBX_StartServer bad scn:%d and psm:0x%x", p_params->scn, p_params->psm); + return OBX_BAD_PARAMS; + } + + if (p_params->max_sessions == 0) + p_params->max_sessions = 1; + + /* allocate a server control block */ + obx_handle = obx_sr_alloc_cb(p_params); + if (obx_handle) + p_cb = &obx_cb.server[obx_handle-1]; + + if (p_cb != NULL) + { + p_scb = &obx_cb.sr_sess[p_cb->sess[0]-1]; + p_scb->ll_cb.port.rx_mtu = p_params->mtu; + if (p_cb->scn) + { + /* open an RFCOMM port to listen for incoming messages */ + /* allocate the port for the first session now. The others will be allocated when needed */ + status = obx_open_port(&p_scb->ll_cb.port, BT_BD_ANY, p_cb->scn); + } + else + { + status = OBX_SUCCESS; + } + + if (status == OBX_SUCCESS) + { + /* If authentication is needed for this server, save the parameters in control block */ + if (p_params->authenticate) + { + p_cb->p_auth = (tOBX_AUTH_PARAMS *)GKI_getbuf(sizeof(tOBX_AUTH_PARAMS)+OBX_MAX_REALM_LEN+1); + if (p_cb->p_auth) + { + p_cb->p_auth->auth_option = p_params->auth_option; + /* adjust realm len, if the given realm is too big */ + size = (p_params->realm_len>OBX_MAX_REALM_LEN) ? OBX_MAX_REALM_LEN : p_params->realm_len; + p_cb->p_auth->realm_len = size; + p_cb->p_auth->realm[0] = p_params->realm_charset; + if (p_params->realm_len && p_params->p_realm) + memcpy(&p_cb->p_auth->realm[1], p_params->p_realm, size); + } + else + status = OBX_NO_RESOURCES; + } + } + + if (status == OBX_SUCCESS) + { + /* if everything is OK, save the other parameters for this server */ + memset(p_cb->target.target, 0, OBX_MAX_TARGET_LEN); + if (p_params->p_target) + { + if (p_params->p_target->len) + { + /* OBX handles who, connection ID headers */ + p_cb->target.len = p_params->p_target->len; + memcpy(p_cb->target.target, p_params->p_target->target, p_params->p_target->len); + } + else + { + /* the regular default server */ + /* the user handles target, who headers. + * OBX handles connection ID header */ + p_cb->target.len = OBX_DEFAULT_TARGET_LEN; + } + } + else + { + /* the one and only default server */ + /* no target, who, connection id headers for this case */ + p_cb->target.len = 0; + } + +// btla-specific ++ + if (p_scb) + { + OBX_TRACE_DEBUG3("OBX_StartServer target len:%d, authenticate:%d handle:0x%x", + p_cb->target.len, p_params->authenticate, p_scb->ll_cb.port.handle); + p_cb->p_cback = p_params->p_cback; + p_scb->state = OBX_SS_NOT_CONNECTED; + + /* give the handle to application */ + *p_handle = p_scb->ll_cb.port.handle; + } +// btla-specific -- + } + else + { + /* otherwise, free the control block */ + obx_sr_free_cb(obx_handle); + } + } + + return status; +} + + +/******************************************************************************* +** +** Function OBX_StopServer +** +** Description This function is to stop this OBEX server from receiving any +** more incoming requests. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_StopServer(tOBX_HANDLE handle) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_SR_CB *p_cb = obx_sr_get_cb(handle); + tOBX_SR_SESS_CB *p_scb; + int xx; + tOBX_SPND_CB *p_spndcb; + + if (p_cb) + { + /* Process suspended session if necessary */ + if (p_cb->p_suspend) + { + for (xx=0, p_spndcb=p_cb->p_suspend; xx<p_cb->max_suspend; xx++, p_spndcb++) + { + if (p_spndcb->state) + { + btu_stop_timer (&p_spndcb->stle); + } + } + GKI_freebuf (p_cb->p_suspend); + } + + for (xx=0; xx < p_cb->num_sess && p_cb->sess[xx]; xx ++) + { + p_scb = &obx_cb.sr_sess[p_cb->sess[xx]-1]; + if (p_scb->ll_cb.comm.id) + { + OBX_DisconnectRsp(handle, OBX_RSP_SERVICE_UNAVL, NULL); + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + RFCOMM_RemoveServer(p_scb->ll_cb.port.port_handle); + } + } + + if (p_cb->psm) + L2CA_DEREGISTER (p_cb->psm); + + obx_sr_free_cb (handle); + } + else + status = OBX_BAD_HANDLE; + return status; +} + +/******************************************************************************* +** +** Function OBX_AddSuspendedSession +** +** Description This function is to add the session information for a previously +** suspended reliable session to the server control block +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_AddSuspendedSession(tOBX_HANDLE shandle, BD_ADDR peer_addr, UINT8 *p_sess_info, + UINT32 timeout, UINT8 ssn, UINT32 offset) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_SR_CB *p_cb = obx_sr_get_cb(shandle); + UINT16 size; + UINT8 xx; + tOBX_SPND_CB *p_spndcb; + INT32 ticks = 0x7FFFFFFF, remain_ticks; + BOOLEAN added = FALSE; + UINT8 saved_xx = 0; + + OBX_TRACE_DEBUG2("OBX_AddSuspendedSession BDA: %06x%06x", + (peer_addr[0]<<16)+(peer_addr[1]<<8)+peer_addr[2], + (peer_addr[3]<<16)+(peer_addr[4]<<8)+peer_addr[5]); + if (p_cb && p_sess_info && p_cb->max_suspend) + { + if (p_cb->p_suspend == NULL) + { + size = p_cb->max_suspend * sizeof (tOBX_SPND_CB); + p_cb->p_suspend = (tOBX_SPND_CB *)GKI_getbuf( size); + memset (p_cb->p_suspend, 0, size); + } + + if (p_cb->p_suspend) + { + for (xx=0, p_spndcb=p_cb->p_suspend; xx<p_cb->max_suspend; xx++, p_spndcb++) + { + OBX_TRACE_DEBUG4("[%d] state: %d, ssn:%d BDA: %08x", xx, p_spndcb->state, p_spndcb->ssn, + (p_spndcb->peer_addr[2]<<24)+(p_spndcb->peer_addr[3]<<16)+(p_spndcb->peer_addr[4]<<8)+p_spndcb->peer_addr[5]); + if (p_spndcb->state == OBX_SS_NULL || memcmp(p_spndcb->peer_addr, peer_addr, BD_ADDR_LEN) == 0) + { + added = TRUE; + break; + } + else if (p_spndcb->state != OBX_SS_NULL && ticks) + { + if (p_spndcb->stle.param == 0) + { + /* this entry has infinite timeout; just use it */ + ticks = 0; + saved_xx = xx; + OBX_TRACE_DEBUG1("[%d] infinite timeout", xx ); + } + /* find the entry the expires in the shortest time */ + else + { + remain_ticks = btu_remaining_time(&p_spndcb->stle); + OBX_TRACE_DEBUG2("[%d] remain_ticks: %d", xx, remain_ticks ); + if (remain_ticks < ticks) + { + ticks = remain_ticks; + saved_xx = xx; + } + } + } + } + + if (!added) + { + /* if cannot use an empty/or reuse an existing entry, use the one expires soon */ + added = TRUE; + xx = saved_xx; /* this is for debug trace; don't optimize */ + p_spndcb = &p_cb->p_suspend[xx]; + OBX_TRACE_DEBUG1("reuse entry [%d]", xx ); + } + + if (added) + { + memcpy (p_spndcb->sess_info, p_sess_info, OBX_SESSION_INFO_SIZE); + p_spndcb->state = p_sess_info[OBX_SESSION_INFO_ST_IDX]; + p_spndcb->ssn = ssn; + p_spndcb->offset = offset; + OBX_TRACE_DEBUG6("[%d] timeout: %d state:%d ssn:%d offset:%d, BDA: %08x", + xx, timeout, p_spndcb->state, ssn, offset, + (peer_addr[2]<<24)+(peer_addr[3]<<16)+(peer_addr[4]<<8)+peer_addr[5]); + memcpy(p_spndcb->peer_addr, peer_addr, BD_ADDR_LEN); + if (timeout != OBX_INFINITE_TIMEOUT) + { + p_spndcb->stle.param = (UINT32)p_spndcb; + btu_start_timer(&p_spndcb->stle, BTU_TTYPE_OBX_SVR_SESS_TO, timeout); + OBX_TRACE_DEBUG2("timeout: %d ticks:%d", timeout, p_spndcb->stle.ticks); + } + else + p_spndcb->stle.param = 0; + } + } + } + else + status = OBX_BAD_HANDLE; + return status; +} + +/******************************************************************************* +** +** Function OBX_ConnectRsp +** +** Description This function is called to send the response to a Connect +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_ConnectRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_SR_SESS_CB *p_scb = obx_sr_get_scb(shandle); + tOBX_SR_CB *p_cb = obx_sr_get_cb(shandle); + + if (p_scb) + { + p_pkt = obx_conn_rsp(p_cb, p_scb, rsp_code, p_pkt); + + obx_ssm_event(p_scb, OBX_CONNECT_CFM_SEVT, p_pkt); + } + else + { + OBX_TRACE_DEBUG1("OBX_ConnectRsp Bad Handle: 0x%x", shandle); + status = OBX_BAD_HANDLE; + } + return status; +} + +/******************************************************************************* +** +** Function OBX_SessionRsp +** +** Description This function is called to respond to a request to create a reliable session. +** +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_SessionRsp(tOBX_HANDLE shandle, UINT8 rsp_code, UINT8 ssn, UINT32 offset, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_SR_SESS_CB *p_scb = obx_sr_get_scb(shandle); + UINT8 *p; + BT_HDR *p_rsp = NULL; + tOBX_TRIPLET triplet[5]; + UINT8 data[12]; + UINT8 num_trip = 0; + UINT32 timeout; + tOBX_SESS_ST old_sess_st; + + OBX_TRACE_API0("OBX_SessionRsp"); + if (p_scb) + { + old_sess_st = p_scb->sess_st; + p_rsp = OBX_HdrInit(p_scb->handle, OBX_MIN_MTU); + if (p_rsp) + { + p = (UINT8 *) (p_rsp + 1) + p_rsp->offset; + /* response packet always has the final bit set */ + *p++ = (rsp_code | OBX_FINAL); + p_rsp->len = 3; + p = data; + if (rsp_code == OBX_RSP_OK) + { + switch (p_scb->sess_st) + { + case OBX_SESS_CREATE: + case OBX_SESS_RESUME: + GKI_freebuf (p_rsp); + if (p_pkt) + GKI_freebuf (p_pkt); + OBX_TRACE_DEBUG0("OBX_SessionRsp do not need to be called for CREATE and RESUME"); + return OBX_SUCCESS; + case OBX_SESS_SUSPEND: + p_scb->sess_st = OBX_SESS_SUSPENDED; + p = &p_scb->sess_info[OBX_SESSION_INFO_TO_IDX]; + BE_STREAM_TO_UINT32(timeout, p); + OBX_AddSuspendedSession(p_scb->handle, p_scb->peer_addr, p_scb->sess_info, timeout, ssn, offset); + break; + case OBX_SESS_CLOSE: + p_scb->sess_st = OBX_SESS_NONE; + break; + } + + if (num_trip) + OBX_AddTriplet(p_rsp, OBX_HI_SESSION_PARAM, triplet, num_trip); + if (p_pkt) + { + p = (UINT8 *) (p_rsp + 1) + p_rsp->offset + p_rsp->len; + memcpy (p, ((UINT8 *) (p_pkt + 1) + p_pkt->offset), p_pkt->len); + p_rsp->len += p_pkt->len; + } + } + p = (UINT8 *) (p_rsp + 1) + p_rsp->offset + 1; + UINT16_TO_BE_STREAM(p, p_rsp->len); + + p_rsp->event = OBX_SESSION_CFM_SEVT + 1; + } + OBX_TRACE_DEBUG3("Rsp sess_st:%d->%d status:%d", old_sess_st, p_scb->sess_st, status); + + obx_ssm_event(p_scb, OBX_SESSION_CFM_SEVT, p_rsp); + /* clear the "previous" session state as required by earlier comment */ + p_scb->param.sess.sess_st = 0; + } + else + status = OBX_BAD_HANDLE; + + if (p_pkt) + { + GKI_freebuf (p_pkt); + } + return status; +} + +/******************************************************************************* +** +** Function obx_prepend_rsp_msg +** +** Description This function is called to add response code and connection ID +** to the given OBEX message +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS obx_prepend_rsp_msg(tOBX_HANDLE shandle, tOBX_SR_EVENT event, UINT8 rsp_code, BT_HDR *p_pkt) +{ + tOBX_STATUS status = OBX_SUCCESS; + tOBX_SR_SESS_CB *p_scb = obx_sr_get_scb(shandle); + UINT8 msg[OBX_HDR_OFFSET]; + UINT8 *p = msg; + + if (p_scb) + { + /* response packets always have the final bit set */ + *p++ = (rsp_code | OBX_FINAL); + p += OBX_PKT_LEN_SIZE; + + /* add session sequence number, if session is active */ + if (p_scb->sess_st == OBX_SESS_ACTIVE || p_scb->sess_st == OBX_SESS_SUSPENDING) + { + *p++ = OBX_HI_SESSION_SN; + *p++ = (p_scb->ssn+1); + } + + if (event == OBX_DISCNT_CFM_SEVT) + p_scb->conn_id = 0; + + if (p_scb->srm & OBX_SRM_REQING) + { + p_scb->srm &= ~OBX_SRM_REQING; + if (rsp_code == OBX_RSP_CONTINUE) + { + p_scb->srm |= OBX_SRM_ENGAGE; + *p++ = OBX_HI_SRM; + *p++ = OBX_HV_SRM_ENABLE; + + if (event == OBX_PUT_CFM_SEVT) + p_scb->srm |= OBX_SRM_NEXT; + } + } + + p_pkt = obx_sr_prepend_msg(p_pkt, msg, (UINT16)(p - msg) ); + /* this event code needs to be set up properly for flow control reasons */ + p_pkt->event = event+1; + obx_ssm_event(p_scb, event, p_pkt); + } + else + status = OBX_BAD_HANDLE; + + return status; +} + +/******************************************************************************* +** +** Function OBX_SetPathRsp +** +** Description This function is called to send the response to a Set Path +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_SetPathRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_SETPATH_CFM_SEVT, rsp_code, p_pkt); +} + + +/******************************************************************************* +** +** Function OBX_PutRsp +** +** Description This function is called to send the response to a Put +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_PutRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_PUT_CFM_SEVT, rsp_code, p_pkt); +} + + +/******************************************************************************* +** +** Function OBX_GetRsp +** +** Description This function is called to send the response to a Get +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_GetRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_GET_CFM_SEVT, rsp_code, p_pkt); +} + + +/******************************************************************************* +** +** Function OBX_AbortRsp +** +** Description This function is called to send the response to an Abort +** Request from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_AbortRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_ABORT_CFM_SEVT, rsp_code, p_pkt); +} + +/******************************************************************************* +** +** Function OBX_ActionRsp +** +** Description This function is called to respond to an Action command Request +** from an OBEX client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_ActionRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_ACTION_CFM_SEVT, rsp_code, p_pkt); +} + +/******************************************************************************* +** +** Function OBX_DisconnectRsp +** +** Description This function is called to send the response to a Disconnect +** Request from an OBEX client. +** This function can also be used to force close the transport +** to a connected client. +** +** Returns OBX_SUCCESS, if successful. +** OBX_BAD_HANDLE, if the handle is not valid. +** +*******************************************************************************/ +tOBX_STATUS OBX_DisconnectRsp(tOBX_HANDLE shandle, UINT8 rsp_code, BT_HDR *p_pkt) +{ + return obx_prepend_rsp_msg(shandle, OBX_DISCNT_CFM_SEVT, rsp_code, p_pkt); +} +#endif diff --git a/stack/obx/obx_ssm.c b/stack/obx/obx_ssm.c new file mode 100644 index 0000000..2e06404 --- /dev/null +++ b/stack/obx/obx_ssm.c @@ -0,0 +1,365 @@ +/***************************************************************************** +** +** Name: obx_ssm.c +** +** File: OBEX Server State Machine and Control Block Access Functions +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> + +#include "btu.h" /* for timer */ +#include "obx_int.h" + +/* OBEX Server Action Functions Enums (must match obx_sr_action [below] */ +enum +{ + OBX_SA_CLOSE_PORT, + OBX_SA_CONNECTION_ERROR, + OBX_SA_STATE, + OBX_SA_CONNECT_IND, + OBX_SA_WC_CONN_IND, + OBX_SA_NC_TO, + OBX_SA_CONNECT_RSP, + OBX_SA_AUTH_IND, + OBX_SA_SND_RSP, + OBX_SA_SAVE_REQ, + OBX_SA_SND_PART, + OBX_SA_REJ_REQ, + OBX_SA_ABORT_RSP, + OBX_SA_OP_RSP, + OBX_SA_GET_IND, + OBX_SA_GET_REQ, + OBX_SA_SESSION_IND, + OBX_SA_SESS_CONN_IND, + OBX_SA_WC_SESS_IND, + OBX_SA_SESSION_RSP, + OBX_SA_PUT_IND, + OBX_SA_SRM_PUT_REQ, + OBX_SA_SRM_PUT_RSP, + OBX_SA_SRM_GET_FCS, + OBX_SA_SRM_GET_RSP, + OBX_SA_SRM_GET_REQ, + OBX_SA_CLEAN_PORT +}; + +/* OBEX Server Action Functions */ +static const tOBX_SR_ACT obx_sr_action[] = +{ + obx_sa_close_port, + obx_sa_connection_error, + obx_sa_state, + obx_sa_connect_ind, + obx_sa_wc_conn_ind, + obx_sa_nc_to, + obx_sa_connect_rsp, + obx_sa_auth_ind, + obx_sa_snd_rsp, + obx_sa_save_req, + obx_sa_snd_part, + obx_sa_rej_req, + obx_sa_abort_rsp, + obx_sa_op_rsp, + obx_sa_get_ind, + obx_sa_get_req, + obx_sa_session_ind, + obx_sa_sess_conn_ind, + obx_sa_wc_sess_ind, + obx_sa_session_rsp, + obx_sa_put_ind, + obx_sa_srm_put_req, + obx_sa_srm_put_rsp, + obx_sa_srm_get_fcs, + obx_sa_srm_get_rsp, + obx_sa_srm_get_req, + obx_sa_clean_port +}; + +/************ OBX Server FSM State/Event Indirection Table **************/ +/* obx_ssm_event() first looks at obx_ssm_entry_map[][] to get an entry of the event of a particular state + * 0 means the event in the current state is ignored. + * a number with 0x80 bit set, use obx_sr_all_table[][] as the "state table". + * other numbers, look up obx_sr_main_state_table[] for the state table of current state. + * + * once the state table is determined, + * look up the "action" column to find the associated action function + * and the "next state" column to find the "next state" candidate. + * + * The actual next state could be either the state in the "next state" column + * or the state returned from the action function. + */ +static const UINT8 obx_ssm_entry_map[][OBX_SS_MAX-1] = +{ +/* state name: NtCon SesIn CntIn WtAut AutIn Conn DscIn StpIn ActIn AbtIn PutIn GetIn Put Get PutS GetS Part WtCls */ +/* CONN_R */{ 1, 0x82, 0x82, 1, 0x82, 0x86, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 ,1 }, +/* SESS_R */{ 3, 0x82, 0x82, 0x82, 0x82, 0x88, 3, 0x85, 0x85, 0x85, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 1 ,3 }, +/* DISCNT_R */{ 0x86, 0x82, 0x82, 0x82, 0x82, 0x85, 0x82, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 1 ,0x86 }, +/* PUT_R */{ 0x86, 0x82, 0x82, 0x82, 0x82, 1, 0x87, 0x87, 0x87, 0x82, 0x87, 0x87, 1, 0x82, 1, 0x82, 0x82 ,0x86 }, +/* GET_R */{ 0x86, 0x82, 0x82, 0x82, 0x82, 2, 0x87, 0x87, 0x87, 0x82, 0x87, 0x87, 0x82, 1, 0x82, 1, 0x82 ,0x86 }, +/* SETPATH_R*/{ 0x86, 0x82, 0x82, 0x82, 0x82, 3, 0x87, 0x87, 0x87, 0x82, 0x87, 0x87, 0x82, 0x82, 0x82, 0x82, 0x82 ,0x86 }, +/* ACTION_R */{ 0x86, 0x82, 0x82, 0x82, 0x82, 4, 0x87, 0x87, 0x87, 0x82, 0x87, 0x87, 0x82, 0x82, 0x82, 0x82, 0x82 ,0x86 }, +/* ABORT_R */{ 0x86, 0x82, 0x82, 0x82, 0x82, 0x86, 0x82, 0x82, 0x82, 0x82, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 1 ,0x86 }, +/* CONN_C */{ 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 }, +/* SESS_C */{ 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 }, +/* DISCNT_C */{ 0, 0x82, 0x82, 0x82, 0x82, 0x82, 1, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 ,0 }, +/* PUT_C */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0 ,0 }, +/* GET_C */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 2, 0 ,0 }, +/* SETPATH_C*/{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 }, +/* ACTION_C */{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ,0 }, +/* ABORT_C */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 ,0 }, +/* PORT_CLS */{ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83 ,0x83 }, +/* FCS_SET */{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2 ,0 }, +/* STATE */{ 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84 ,0x84 }, +/* TIMEOUT */{ 2, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,2 }, +/* BAD_REQ */{ 0x86, 0x82, 0x82, 0x82, 0x82, 0x86, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82 ,0x86 }, +/* TX_EMPTY */{ 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 } +}; + +static const UINT8 obx_sr_all_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* ABORT_R */{OBX_SM_NO_ACTION, OBX_SS_ABORT_INDICATED }, +/* misc */{OBX_SA_CLOSE_PORT, OBX_SS_NOT_CONNECTED }, +/* PORT_CLS */{OBX_SA_CONNECTION_ERROR, OBX_SS_NOT_CONNECTED }, +/* STATE */{OBX_SA_STATE, OBX_SS_NULL }, +/* DISCNT_R */{OBX_SM_NO_ACTION, OBX_SS_DISCNT_INDICATED }, +/* misc */{OBX_SA_REJ_REQ, OBX_SS_NULL }, +/* illegalop*/{OBX_SA_CLEAN_PORT, OBX_SS_NOT_CONNECTED }, +/* SESS_R */{OBX_SA_SESSION_IND, OBX_SS_SESS_INDICATED } +}; + +static const UINT8 obx_sr_not_conn_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_R */{OBX_SA_CONNECT_IND, OBX_SS_CONN_INDICATED }, +/* TIMEOUT */{OBX_SA_NC_TO, OBX_SS_NOT_CONNECTED}, +/* SESS_R */{OBX_SA_SESS_CONN_IND, OBX_SS_SESS_INDICATED } +}; + +static const UINT8 obx_sr_connect_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_C */{OBX_SA_CONNECT_RSP, OBX_SS_CONNECTED } +}; + +static const UINT8 obx_sr_session_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* SESS_R */{OBX_SA_SESSION_RSP, OBX_SS_NOT_CONNECTED } +}; + +static const UINT8 obx_sr_wait_auth_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_R */{OBX_SA_AUTH_IND, OBX_SS_AUTH_INDICATED }, +/* TIMEOUT */{OBX_SA_CLOSE_PORT, OBX_SS_NOT_CONNECTED} +}; + +static const UINT8 obx_sr_conn_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_SA_PUT_IND, OBX_SS_PUT_INDICATED }, +/* GET_R */{OBX_SA_GET_IND, OBX_SS_GET_INDICATED }, +/* SETPATH_R*/{OBX_SM_NO_ACTION, OBX_SS_SETPATH_INDICATED }, +/* ACTION_R */{OBX_SM_NO_ACTION, OBX_SS_ACTION_INDICATED } +}; + +static const UINT8 obx_sr_disconnect_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* DISCNT_C */{OBX_SA_SND_RSP, OBX_SS_WAIT_CLOSE }, +/* TIMEOUT */{OBX_SA_CLOSE_PORT, OBX_SS_NOT_CONNECTED}, +/* SESS_R */{OBX_SA_SESSION_IND, OBX_SS_DISCNT_INDICATED} +}; + +static const UINT8 obx_sr_setpath_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* SETPATH_C*/{OBX_SA_SND_RSP, OBX_SS_CONNECTED } +}; + +static const UINT8 obx_sr_abort_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* GET/PUT_C*/{OBX_SA_OP_RSP, OBX_SS_ABORT_INDICATED }, +/* ABORT_C */{OBX_SA_ABORT_RSP, OBX_SS_CONNECTED } +}; + +static const UINT8 obx_sr_put_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_C */{OBX_SA_SND_RSP, OBX_SS_PUT_TRANSACTION } +}; + +static const UINT8 obx_sr_get_ind_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* GET_C */{OBX_SA_SND_RSP, OBX_SS_GET_TRANSACTION } +}; + +static const UINT8 obx_sr_put_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_SM_NO_ACTION, OBX_SS_PUT_INDICATED } +}; + +static const UINT8 obx_sr_get_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* GET_R */{OBX_SA_GET_REQ, OBX_SS_GET_INDICATED } +}; + +static const UINT8 obx_sr_put_s_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* PUT_R */{OBX_SA_SRM_PUT_REQ, OBX_SS_PUT_SRM }, +/* PUT_C */{OBX_SA_SRM_PUT_RSP, OBX_SS_PUT_SRM } +}; + +static const UINT8 obx_sr_get_s_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* GET_R */{OBX_SA_SRM_GET_REQ, OBX_SS_GET_SRM }, +/* GET_C */{OBX_SA_SRM_GET_RSP, OBX_SS_GET_SRM }, +/* FCS_SET */{OBX_SA_SRM_GET_FCS, OBX_SS_GET_SRM } +}; + +static const UINT8 obx_sr_part_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* ABORT_R */{OBX_SA_SAVE_REQ, OBX_SS_NULL },/* and DISCNT_R */ +/* FCS_SET */{OBX_SA_SND_PART, OBX_SS_NULL } +}; + +static const UINT8 obx_sr_wait_close_table[][OBX_SM_NUM_COLS] = { +/* Event Action Next State */ +/* CONN_R */{OBX_SA_WC_CONN_IND, OBX_SS_CONN_INDICATED }, +/* TIMEOUT */{OBX_SA_NC_TO, OBX_SS_NOT_CONNECTED}, +/* SESS_R */{OBX_SA_WC_SESS_IND, OBX_SS_SESS_INDICATED } +}; + +static const tOBX_SM_TBL obx_sr_main_state_table[] = { + obx_sr_not_conn_table, + obx_sr_session_ind_table, + obx_sr_connect_ind_table, + obx_sr_wait_auth_table, + obx_sr_connect_ind_table, /* same table for auth ind */ + obx_sr_conn_table, + obx_sr_disconnect_ind_table, + obx_sr_setpath_ind_table, + obx_sr_setpath_ind_table, /* same action table for action_ind */ + obx_sr_abort_ind_table, + obx_sr_put_ind_table, + obx_sr_get_ind_table, + obx_sr_put_table, + obx_sr_get_table, + obx_sr_put_s_table, + obx_sr_get_s_table, + obx_sr_part_table, + obx_sr_wait_close_table +}; + +/******************************************************************************* +** +** Function obx_ssm_event +** +** Description Handle events to the server state machine. It looks up the entry +** in the obx_ssm_entry_map array. If it is a valid entry, it gets +** the state table.Set the next state, if not NULL state.Execute +** the action function according to the state table. If the state +** returned by action function is not NULL state, adjust the new +** state to the returned state.If (api_evt != MAX), call callback +** function. +** +** Returns void. +** +*******************************************************************************/ +void obx_ssm_event(tOBX_SR_SESS_CB *p_scb, tOBX_SR_EVENT event, BT_HDR *p_msg) +{ + UINT8 curr_state = p_scb->state; + tOBX_SM_TBL state_table; + UINT8 action, entry; + tOBX_SR_STATE act_state = OBX_SS_NULL; + UINT8 *p_data; + UINT16 len; + BT_HDR *p_dummy; + tOBX_EVENT api_evt; + tOBX_SR_CB *p_cb; +#if 0 + UINT8 srm; +#endif + + if( curr_state == OBX_SS_NULL || curr_state >= OBX_SS_MAX) + { + OBX_TRACE_WARNING1( "Invalid state: %d", curr_state) ; + if(p_msg) + GKI_freebuf(p_msg); + return; + } + + OBX_TRACE_DEBUG6( "For Server SHandle 0x%x, State: %s, Event: %s/%d srm:0x%x ssn:%d", + p_scb->ll_cb.comm.handle, obx_sr_get_state_name( p_scb->state ), + obx_sr_get_event_name(event), event, p_scb->srm, p_scb->ssn ) ; + + /* look up the state table for the current state */ + /* lookup entry /w event & curr_state */ + /* If entry is ignore, return. + * Otherwise, get state table (according to curr_state or all_state) */ + /* coverity [index_parm] */ + if( (entry = obx_ssm_entry_map[event][curr_state-1]) != OBX_SM_IGNORE ) + { + if(entry&OBX_SM_ALL) + { + entry &= OBX_SM_ENTRY_MASK; + state_table = obx_sr_all_table; + } + else + state_table = obx_sr_main_state_table[curr_state-1]; + } + else + { + OBX_TRACE_WARNING2( "Ignore event %d in state %d", event, curr_state ); + if(p_msg) + GKI_freebuf(p_msg); + return; + } + + /* Get possible next state from state table. */ + if( state_table[entry-1][OBX_SME_NEXT_STATE] != OBX_CS_NULL ) + p_scb->state = state_table[entry-1][OBX_SME_NEXT_STATE]; + p_scb->prev_state = curr_state; + OBX_TRACE_DEBUG3( "possible new state = %s/%s/%d", + obx_sr_get_state_name(p_scb->state), obx_sr_get_state_name( p_scb->prev_state ), p_scb->prev_state) ; + + /* If action is not ignore, clear param, exec action and get next state. + * The action function may set the Param for cback. + * Depending on param, call cback or free buffer. */ + /* execute action */ + action = state_table[entry-1][OBX_SME_ACTION]; + if (action != OBX_SM_NO_ACTION) + { + act_state = (*obx_sr_action[action])(p_scb, p_msg); + } + + /* adjust next state, if it needs to use the new state returned from action function */ + if( act_state != OBX_CS_NULL) + { + p_scb->state = act_state; + OBX_TRACE_DEBUG1( "new state = %s (action)", obx_sr_get_state_name( p_scb->state ) ) ; + } + + if(p_scb->api_evt) + { + api_evt = p_scb->api_evt; + p_scb->api_evt = OBX_NULL_EVT; + /* we do not want the operation to be challenged by the client */ + if( event <= OBX_SEVT_MAX_REQ && event != OBX_CONNECT_REQ_SEVT && + OBX_ReadByteStrHdr(p_msg, OBX_HI_CHALLENGE, &p_data, &len, 0) == TRUE) + { + /* send bad request response */ + p_dummy = obx_build_dummy_rsp(p_scb, OBX_RSP_BAD_REQUEST); + event += OBX_SEVT_DIFF_REQ_CFM; + obx_ssm_event(p_scb, event, p_dummy); + GKI_freebuf(p_msg); + } + else + { + p_cb = &obx_cb.server[p_scb->handle - 1]; + (p_cb->p_cback) (p_scb->ll_cb.comm.handle, api_evt, p_scb->param, p_msg); + } + memset(&p_scb->param, 0, sizeof (p_scb->param) ); + } + else if(action == OBX_SM_NO_ACTION && p_msg) + GKI_freebuf(p_msg); + + + OBX_TRACE_DEBUG2( "result state = %s ssn:%d", obx_sr_get_state_name( p_scb->state ), p_scb->ssn ) ; +} + + diff --git a/stack/obx/obx_utils.c b/stack/obx/obx_utils.c new file mode 100644 index 0000000..785df18 --- /dev/null +++ b/stack/obx/obx_utils.c @@ -0,0 +1,979 @@ +/***************************************************************************** +** +** Name: obx_utils.c +** +** File: OBEX common utility functions +** +** Copyright (c) 2003-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +** +*****************************************************************************/ +#include <string.h> +#include <stdio.h> +#include "wcassert.h" + + +#include "bt_target.h" +#include "obx_int.h" +#include "port_api.h" +#include "sdpdefs.h" +#include "l2c_api.h" + +const tOBX_EVENT obx_sm_evt_to_api_evt[OBX_MAX_EVT_MAP_NUM] = +{ + OBX_CONNECT_REQ_EVT, + OBX_SESSION_REQ_EVT, /* A Crease Session or Resume Session request is received by the server. Call OBX_SessionRsp(). */ + OBX_DISCONNECT_REQ_EVT, + OBX_PUT_REQ_EVT, + OBX_GET_REQ_EVT, + OBX_SETPATH_REQ_EVT, + OBX_ACTION_REQ_EVT, /* An Action request is received by the server. Call OBX_ActionRsp(). */ + OBX_ABORT_REQ_EVT +}; + + +const UINT8 obx_rsp_code[] = +{ + OBX_RSP_CONTINUE, /* 0x10 Continue */ + OBX_RSP_OK, /* 0x20 OK, Success */ + OBX_RSP_CREATED, /* 0x21 Created */ + OBX_RSP_ACCEPTED, /* 0x22 Accepted */ + OBX_RSP_NON_AUTH_INFO, /* 0x23 Non-Authoritative Information */ + OBX_RSP_NO_CONTENT, /* 0x24 No Content */ + OBX_RSP_RESET_CONTENT, /* 0x25 Reset Content */ + OBX_RSP_PART_CONTENT, /* 0x26 Partial Content */ + OBX_RSP_MULTI_CHOICES, /* 0x30 Multiple Choices */ + OBX_RSP_MVD_PERM, /* 0x31 Moved Permanently */ + OBX_RSP_MVD_TEMP, /* 0x32 Moved temporarily */ + OBX_RSP_SEE_OTHER, /* 0x33 See Other */ + OBX_RSP_NOT_MODIFIED, /* 0x34 Not modified */ + OBX_RSP_USE_PROXY, /* 0x35 Use Proxy */ + OBX_RSP_BAD_REQUEST, /* 0x40 Bad Request - server couldn't understand request */ + OBX_RSP_UNAUTHORIZED, /* 0x41 Unauthorized */ + OBX_RSP_PAYMENT_REQD, /* 0x42 Payment required */ + OBX_RSP_FORBIDDEN, /* 0x43 Forbidden - operation is understood but refused */ + OBX_RSP_NOT_FOUND, /* 0x44 Not Found */ + OBX_RSP_NOT_ALLOWED, /* 0x45 Method not allowed */ + OBX_RSP_NOT_ACCEPTABLE, /* 0x46 Not Acceptable */ + OBX_RSP_PROXY_AUTH_REQD, /* 0x47 Proxy Authentication required */ + OBX_RSP_REQUEST_TIMEOUT, /* 0x48 Request Time Out */ + OBX_RSP_CONFLICT, /* 0x49 Conflict */ + OBX_RSP_GONE, /* 0x4A Gone */ + OBX_RSP_LENGTH_REQD, /* 0x4B Length Required */ + OBX_RSP_PRECONDTN_FAILED, /* 0x4C Precondition failed */ + OBX_RSP_REQ_ENT_2_LARGE, /* 0x4D Requested entity too large */ + OBX_RSP_REQ_URL_2_LARGE, /* 0x4E Request URL too large */ + OBX_RSP_UNSUPTD_TYPE, /* 0x4F Unsupported media type */ + OBX_RSP_INTRNL_SRVR_ERR, /* 0x50 Internal Server Error */ + OBX_RSP_NOT_IMPLEMENTED, /* 0x51 Not Implemented */ + OBX_RSP_BAD_GATEWAY, /* 0x52 Bad Gateway */ + OBX_RSP_SERVICE_UNAVL, /* 0x53 Service Unavailable */ + OBX_RSP_GATEWAY_TIMEOUT, /* 0x54 Gateway Timeout */ + OBX_RSP_HTTP_VER_NOT_SUPTD, /* 0x55 HTTP version not supported */ + OBX_RSP_DATABASE_FULL, /* 0x60 Database Full */ + OBX_RSP_DATABASE_LOCKED, /* 0x61 Database Locked */ + OBX_RSP_DEFAULT +}; + +static void obx_read_mtu(BT_HDR *p_pkt, tOBX_HANDLE handle, tOBX_CONN_EVT *p_evt); + +/******************************************************************************* +** Function obx_read_srm +** Description read the SRM and SRM_PARAM headers from the packet received from +** peer and set the control block data member accordingly +** Return UINT8 +*******************************************************************************/ +UINT8 obx_read_srm (tOBX_SRM *p_srm, BOOLEAN is_client, BT_HDR *p_pkt) +{ + UINT8 srm, srmp = 0, ret_srmp=0; + UINT8 old_srm = *p_srm; + BOOLEAN allowed = FALSE, clear = TRUE; + + OBX_TRACE_DEBUG1("obx_read_srm srm:0x%x", *p_srm ); + if (*p_srm) + { + /* if the SRM enable request is not granted in the next packet, the request is not valid any more + * clear the requesting flag */ + *p_srm &= ~OBX_SRM_REQING; + + if (OBX_Read1ByteHdr(p_pkt, OBX_HI_SRM, &srm)) + { + if (srm == OBX_HV_SRM_ENABLE) + { + if (is_client) + { + if (old_srm & OBX_SRM_REQING) + { + *p_srm |= OBX_SRM_ENGAGE; + allowed = TRUE; + } + } + else /* is server */ + { + *p_srm |= OBX_SRM_REQING; + allowed = TRUE; + } + } + OBX_TRACE_DEBUG3("SRM :0x%x srm:0x%x old_srm:0x%x", srm, *p_srm, old_srm ); + } + + if (!allowed) + allowed = old_srm & OBX_SRM_PARAM_AL; + + if (OBX_Read1ByteHdr(p_pkt, OBX_HI_SRM_PARAM, &srmp)) + { + if ((srmp == OBX_HV_SRM_PARAM_WAIT) && allowed) + { + ret_srmp = OBX_SRMP_WAIT; + *p_srm |= OBX_SRM_PARAM_AL; + clear = FALSE; + } + } + OBX_TRACE_DEBUG4("SRM_PARAM :0x%x srm:0x%x allowed:%d clear:%d", srmp, *p_srm, allowed, clear ); + + /* once the SRMP header is not used, it should be ignored for the rest of the transaction */ + if (clear) + *p_srm &= ~OBX_SRM_PARAM_AL; + } + + return ret_srmp; +} + +/******************************************************************************* +** Function obx_add_timeout +** Description add the timeout triplet +** +** Return UINT8 +*******************************************************************************/ +UINT8 obx_add_timeout (tOBX_TRIPLET *p_trip, UINT32 timeout, tOBX_SESS_EVT *p_param) +{ + UINT8 *p; + UINT8 ret = 0; + + if (timeout != OBX_INFINITE_TIMEOUT) + { + p_trip->tag = OBX_TAG_SESS_PARAM_TOUT; + p_trip->len = OBX_TIMEOUT_SIZE; + p = p_trip->p_array; + UINT32_TO_BE_STREAM(p, timeout); + ret = 1; + } + p_param->timeout = timeout; + return ret; +} + +/******************************************************************************* +** Function obx_read_timeout +** Description add the timeout triplet +** +** Return void +*******************************************************************************/ +void obx_read_timeout (tOBX_TRIPLET *p_trip, UINT8 num, UINT32 *p_timeout, UINT8 *p_toa) +{ + UINT8 ind; + UINT8 *p; + UINT32 tmp; + + p = p_toa; + BE_STREAM_TO_UINT32(tmp, p); + OBX_TRACE_DEBUG2("obx_read_timeout %d/%d", *p_timeout, tmp); + if (*p_timeout == 0) + *p_timeout = tmp; + ind = obx_read_triplet(p_trip, num, OBX_TAG_SESS_PARAM_TOUT); + if ((ind != num) && (p_trip[ind].len == OBX_TIMEOUT_SIZE)) + { + p = p_trip[ind].p_array; + BE_STREAM_TO_UINT32(tmp, p); + if (tmp < (*p_timeout)) + { + (*p_timeout) = tmp; + OBX_TRACE_DEBUG1("new timeout %d", tmp); + } + } + UINT32_TO_BE_STREAM(p_toa, (*p_timeout)); +} + +#if (OBX_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_verify_response +** Description +** Return OBX_BAD_SM_EVT, if bad. +*******************************************************************************/ +UINT8 obx_verify_response(UINT8 opcode, tOBX_RX_HDR *p_rxh) +{ + UINT8 final = (opcode & OBX_FINAL) ? TRUE : FALSE; + int xx = 0; + UINT8 res_code = opcode & ~OBX_FINAL; + + p_rxh->sm_evt = OBX_BAD_SM_EVT; + + /* response packet must have the final bit set */ + if (final == TRUE) + { + if (res_code == OBX_RSP_CONTINUE) + { + p_rxh->sm_evt = OBX_CONT_CFM_CEVT; + } + else + { + /* figure out the kind of response, Continue, OK, or Error */ + while(obx_rsp_code[xx] != OBX_RSP_DEFAULT) + { + if (obx_rsp_code[xx] == res_code) + break; + xx++; + } + + if (obx_rsp_code[xx] != OBX_RSP_DEFAULT) + { + if (obx_rsp_code[xx] <= OBX_MAX_OK_RSP) + p_rxh->sm_evt = OBX_OK_CFM_CEVT; + else + p_rxh->sm_evt = OBX_FAIL_CFM_CEVT; + } + /* else bad response code */ + } + if (p_rxh->sm_evt != OBX_BAD_SM_EVT) + { + p_rxh->code = opcode; + } + } + /* + else final bit not set in response packet -> bad response code + */ + + return p_rxh->sm_evt; +} + +/******************************************************************************* +** +** Function obx_cl_proc_pkt +** +** Description process a packet received from the connected server +** verify that the response is valid +** fill in event parameters +** call csm to process the event +** +** Returns void +** +*******************************************************************************/ +void obx_cl_proc_pkt (tOBX_CL_CB *p_cb, BT_HDR *p_pkt) +{ + tOBX_RX_HDR *p_rxh; + UINT32 conn_id; + BOOLEAN pass = FALSE; + UINT8 xx; + tOBX_TRIPLET triplet[OBX_MAX_NUM_AUTH_TRIPLET]; + UINT8 num_trip = OBX_MAX_NUM_AUTH_TRIPLET; + tOBX_CL_EVENT sm_evt; + UINT8 ssn; + + OBX_TRACE_DEBUG3("obx_cl_proc_pkt 0x%x srm:0x%x, sess_st:%d", p_pkt, p_cb->srm, p_cb->sess_st); + OBX_TRACE_DEBUG1("csm offset:%d", p_cb->param.sess.obj_offset); + + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + p_cb->rsp_code = p_rxh->code & ~OBX_FINAL; + + p_pkt->event = OBX_PUT_RSP_EVT; /* any response */ + /* setup callback event param + memset(&p_cb->param, 0, sizeof(tOBX_EVT_PARAM)); */ + + if (p_cb->state == OBX_CS_CONNECT_REQ_SENT ) + { + /* when a response packet is received in conn_rs state, + * it must be a connect response packet */ + p_pkt->event = OBX_CONNECT_RSP_EVT; + p_cb->param.conn.handle = p_cb->ll_cb.port.handle; + obx_read_mtu(p_pkt, p_cb->ll_cb.port.handle, &(p_cb->param.conn)); + p_cb->ll_cb.port.tx_mtu = p_cb->param.conn.mtu; + + /* save Connection ID */ + if (OBX_Read4ByteHdr(p_pkt, OBX_HI_CONN_ID, &conn_id) == TRUE) + p_cb->conn_id = conn_id; + OBX_TRACE_DEBUG1("Connection ID: 0x%x", p_cb->conn_id ); + } + + if (p_cb->sess_st == OBX_SESS_ACTIVE) + { + /* verify the session sequence number */ + if (OBX_Read1ByteHdr (p_pkt, OBX_HI_SESSION_SN, &ssn)) + { + OBX_TRACE_DEBUG1("ssn:%d", ssn); + p_cb->param.ssn = ssn; + } + } + + sm_evt = p_rxh->sm_evt; + + if (p_cb->wait_auth == TRUE) + { + /* this can only happen for CONNECT */ + if (p_cb->rsp_code == OBX_RSP_OK) + { + /* successful according to server */ + /* verify the digest */ + p_cb->wait_auth = FALSE; + /* The coverity complaints on this function is not correct. + * The value in triplet[] is set/initialized by OBX_ReadTriplet if num_trip returns TRUE. + * leave this unnecessary memset here */ + memset(triplet,0,sizeof(triplet)); + if (OBX_ReadTriplet(p_pkt, OBX_HI_AUTH_RSP, triplet, &num_trip)) + { + for (xx=0; xx<num_trip; xx++) + { + if (triplet[xx].tag == OBX_DIGEST_RSP_TAG) + { + if (memcmp (p_cb->p_auth, triplet[xx].p_array, OBX_DIGEST_SIZE) == 0) + pass = TRUE; + break; + } + } + } + if (pass == FALSE) + { + OBX_TRACE_ERROR0("Failed - server does not provide good digest" ); + p_cb->wait_auth = OBX_WAIT_AUTH_FAIL; + p_cb->rsp_code = OBX_RSP_FAILED; + } + } + } + if (p_cb->p_auth) + GKI_freebuf(p_cb->p_auth); + p_cb->p_auth = NULL; + + p_cb->srmp = obx_read_srm (&p_cb->srm, TRUE, p_pkt); + obx_csm_event(p_cb, sm_evt, p_pkt); +} + +/******************************************************************************* +** +** Function obx_cl_prepend_msg +** +** Description This function is used by API functions to add the data in the +** reserved space in the OBEX packet +** +** Returns void. +** +*******************************************************************************/ +BT_HDR * obx_cl_prepend_msg(tOBX_CL_CB *p_cb, BT_HDR *p_pkt, UINT8 * p_data, UINT16 data_len) +{ + UINT8 *p; + UINT16 len; + + if (p_pkt == NULL) + { + p_pkt = OBX_HdrInit(OBX_HANDLE_NULL, OBX_MIN_MTU); + len = data_len; + } + else + { + len = p_pkt->len + data_len; + } + + WC_ASSERT(p_pkt->offset >= data_len); + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset - data_len; + memcpy(p, p_data, data_len); + p++; + /* adjust the packet len */ + UINT16_TO_BE_STREAM(p, len); + p_pkt->len += data_len; + p_pkt->offset -= data_len; + p_pkt->layer_specific -= data_len; + + return p_pkt; +} +#endif /* OBX_CLIENT_INCLUDED */ + +#if (OBX_SERVER_INCLUDED == TRUE) +/******************************************************************************* +** Function obx_verify_request +** Description +** Return OBX_BAD_SM_EVT, if bad. +*******************************************************************************/ +UINT8 obx_verify_request(UINT8 opcode, tOBX_RX_HDR *p_rxh) +{ + UINT8 final = (opcode & OBX_FINAL) ? TRUE : FALSE; + UINT8 req_code = opcode & ~OBX_FINAL; + p_rxh->sm_evt = OBX_BAD_SM_EVT; + + OBX_TRACE_DEBUG2("obx_verify_request opcode 0x%x: final:%d", opcode, final); + switch (req_code) + { + case OBX_REQ_CONNECT: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_CONNECT_REQ_SEVT; + break; + + case OBX_REQ_SESSION: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_SESSION_REQ_SEVT; + break; + + case OBX_REQ_ACTION: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_ACTION_REQ_SEVT; + break; + + case OBX_REQ_DISCONNECT: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_DISCNT_REQ_SEVT; + break; + + case OBX_REQ_PUT: + p_rxh->sm_evt = OBX_PUT_REQ_SEVT; + break; + + case OBX_REQ_GET: + p_rxh->sm_evt = OBX_GET_REQ_SEVT; + break; + + case OBX_REQ_SETPATH: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_SETPATH_REQ_SEVT; + break; + + case OBX_REQ_ABORT: + /* this request must have the final bit set */ + if (final == TRUE) + p_rxh->sm_evt = OBX_ABORT_REQ_SEVT; + break; + } + + if (p_rxh->sm_evt != OBX_BAD_SM_EVT) + { + p_rxh->code = opcode; + } + return p_rxh->sm_evt; +} + +/******************************************************************************* +** +** Function obx_is_get_or_put_cont +** +** Returns TRUE, if it's a GET/PUT continuing continuing +** TRUE, if it's an ABORT +** +*******************************************************************************/ +BOOLEAN obx_is_get_or_put_cont (tOBX_SR_SESS_CB *p_scb, UINT8 req_code) +{ + BOOLEAN is_cont = FALSE; + + if (req_code == OBX_REQ_ABORT) + { + is_cont = TRUE; + } + else if (req_code == OBX_REQ_PUT) + { + if (p_scb->state == OBX_SS_PUT_TRANSACTION || p_scb->state == OBX_SS_PUT_SRM) + { + is_cont = TRUE; + } + } + else if (req_code == OBX_REQ_GET) + { + if (p_scb->state == OBX_SS_GET_TRANSACTION || p_scb->state == OBX_SS_GET_SRM) + { + is_cont = TRUE; + } + } + + return is_cont; +} + +/******************************************************************************* +** +** Function obx_sr_proc_pkt +** +** Description process a packet received from the connected client +** verify that the request is valid +** fill in event parameters +** call ssm to process the event +** +** Returns TRUE, if abort +** +*******************************************************************************/ +BOOLEAN obx_sr_proc_pkt (tOBX_SR_SESS_CB *p_scb, BT_HDR *p_pkt) +{ + tOBX_RX_HDR *p_rxh; + UINT8 *p_body; + UINT16 len; + BOOLEAN end; + UINT32 conn_id = 0; + UINT8 req_code; + BOOLEAN final; + UINT8 ssn, tmp_ssn; + UINT8 num_hdrs, num_body, num_ssn; + UINT8 rsp_code = OBX_RSP_OK; + UINT8 num_id; + BOOLEAN chk_add = FALSE; + + p_rxh = (tOBX_RX_HDR *)(p_pkt + 1); + final = (p_rxh->code & OBX_FINAL) ? TRUE : FALSE; + req_code = p_rxh->code & ~OBX_FINAL; + p_scb->api_evt = (UINT8)p_pkt->event; + memset(&p_scb->param, 0, sizeof(tOBX_EVT_PARAM)); + p_scb->param.get.final = final; + + num_id = OBX_Read4ByteHdr (p_pkt, OBX_HI_CONN_ID, &conn_id); + + OBX_TRACE_DEBUG6("obx_sr_proc_pkt 0x%x srm:0x%x sess_st:%d req_code:0x%x, sm_evt:%d ssn:%d", + p_pkt, p_scb->srm, p_scb->sess_st, req_code, p_rxh->sm_evt, p_scb->ssn); + + num_ssn = OBX_Read1ByteHdr (p_pkt, OBX_HI_SESSION_SN, &ssn); + if (p_scb->srm & OBX_SRM_ABORT) + { + /* OBX_SRM_ABORT bit is set by server only when PUT req /w SRM is rejected before transaction actually ends + * this means we need to ignore follow packets until the next transaction starts */ + if (req_code == OBX_REQ_PUT) + { + /* check if this is the put for the previous transaction */ + num_hdrs = OBX_ReadNumHdrs(p_pkt, &num_body); + OBX_TRACE_DEBUG4("num_hdrs:%d num_body:%d num_id:%d num_ssn:%d", num_hdrs, num_body, num_id, num_ssn); + num_body += num_ssn; + num_body += num_id; + if (num_hdrs <= num_body) + { + p_scb->ssn = ssn+1; + /* it is left-over, drop it. */ + if (p_pkt) + GKI_freebuf (p_pkt); + p_scb->api_evt = OBX_NULL_EVT; + return TRUE; + } + } + /* clear the SRM bits; leave only the enabled bit */ + p_scb->srm &= OBX_SRM_ENABLE; + } + + if ((p_scb->sess_st == OBX_SESS_ACTIVE) && (req_code != OBX_REQ_SESSION) && (req_code != OBX_REQ_ABORT)) + { + rsp_code = OBX_RSP_BAD_REQUEST; + /* verify the session sequence number */ + if (num_ssn) + { + OBX_TRACE_DEBUG2("ssn pkt/cb=%d/%d", ssn, p_scb->ssn); + if (ssn == p_scb->ssn) + { + p_scb->param.ssn = p_scb->ssn; + rsp_code = OBX_RSP_OK; + } + else if (p_scb->srmp & OBX_SRMP_SESS_FST) + { + tmp_ssn = ssn+1; + if (tmp_ssn == p_scb->ssn) + { + p_scb->param.ssn = ssn; + p_scb->ssn = ssn; + rsp_code = OBX_RSP_OK; + } + } + } + p_scb->srmp &= ~OBX_SRMP_SESS_FST; + } + + /* fill event parameter */ + if (req_code == OBX_REQ_CONNECT) + { + p_scb->param.conn.handle = p_scb->handle; + obx_read_mtu(p_pkt, p_scb->ll_cb.comm.handle, &(p_scb->param.conn)); + + p_scb->ll_cb.port.tx_mtu = p_scb->param.conn.mtu; + /* verify the target header and connection ID + * in obx_sa_connect_ind() for OBX_SS_NOT_CONNECTED */ + /* verify the connection ID in obx_sa_auth_ind() for OBX_SS_WAIT_AUTH */ + chk_add = TRUE; + } + else if (req_code == OBX_REQ_SESSION) + { + /* do nothing */ + if (conn_id != 0) + { + OBX_TRACE_ERROR1("Session command should not use Connection ID: 0x%x", conn_id); + p_rxh->sm_evt = OBX_BAD_REQ_SEVT; + } + } + else + { + OBX_TRACE_DEBUG3("Connection ID: 0x%x/0x%x state:%d", p_scb->conn_id, conn_id, p_scb->state ); + /* verify the connection ID */ + if ( (conn_id == p_scb->conn_id) || + (conn_id == 0 && p_scb->conn_id != 0 && obx_is_get_or_put_cont(p_scb, req_code))) + { + /* match: both non-exist or both exist and equal */ + /* connection ID header does not exist, but control block uses connection ID, + * if this is continuation packets for PUT and GET, it's OK */ + if (req_code == OBX_REQ_PUT) + { + p_scb->param.put.final = final; + + /* determine the PUT type */ + p_scb->param.put.type = OBX_PT_PUT; + if (p_scb->state == OBX_SS_CONNECTED && final == TRUE) + { + if (OBX_ReadBodyHdr(p_pkt, &p_body, &len, &end) == FALSE) + { + /* final is set, no BODY or End-of-Body + * -> Delete request */ + p_scb->param.put.type = OBX_PT_DELETE; + } + else if (end == TRUE && len == 0) + { + /* an empty End-of-Body header + * -> Create-Empty request */ + p_scb->param.put.type = OBX_PT_CREATE; + } + } + OBX_TRACE_EVENT1("Put request type: %d", p_scb->param.put.type); + } + else if (req_code == OBX_REQ_SETPATH) + { + p_scb->param.sp.flag = *((UINT8 *)(p_pkt + 1) + p_pkt->offset + OBX_SETPATH_FLAG_OFFSET); + } + } + else + { + /* does not have good connection ID */ + p_rxh->sm_evt = OBX_BAD_REQ_SEVT; + p_scb->api_evt = OBX_NULL_EVT; + } + } + + /* process the SRM header */ + p_scb->srmp |= obx_read_srm (&p_scb->srm, FALSE, p_pkt); + + if (p_rxh->sm_evt != OBX_BAD_REQ_SEVT && req_code != OBX_REQ_ABORT) + { + p_scb->cur_op = req_code; + if (final) + p_scb->cur_op |= OBX_FINAL; + } + + if (rsp_code != OBX_RSP_OK) + { + p_rxh->sm_evt = OBX_BAD_REQ_SEVT; + } + + OBX_TRACE_DEBUG2("rsp_code:0x%x, sm_evt:%d", rsp_code, p_rxh->sm_evt); + + obx_ssm_event(p_scb, p_rxh->sm_evt, p_pkt); + if (chk_add) + { + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + obx_add_port(p_scb->handle); + } + return FALSE; +} + +/******************************************************************************* +** +** Function obx_sr_prepend_msg +** +** Description This function is used by API functions to add the data in the +** reserved space in the OBEX packet +** +** Returns void. +** +*******************************************************************************/ +BT_HDR * obx_sr_prepend_msg(BT_HDR *p_pkt, UINT8 * p_data, UINT16 data_len) +{ + UINT8 *p; + UINT16 len; + + if (p_pkt == NULL) + { + p_pkt = OBX_HdrInit(OBX_HANDLE_NULL, OBX_MIN_MTU); + len = data_len; + } + else + { + len = p_pkt->len + data_len; + } + + WC_ASSERT(p_pkt->offset >= data_len); + p = (UINT8 *)(p_pkt + 1) + p_pkt->offset - data_len; + memcpy(p, p_data, data_len); + p++; + /* adjust the packet len */ + UINT16_TO_BE_STREAM(p, len); + p_pkt->len += data_len; + p_pkt->offset -= data_len; + p_pkt->layer_specific -= data_len; + + return p_pkt; +} +#endif /* OBX_SERVER_INCLUDED */ + +/******************************************************************************* +** +** Function obx_read_mtu +** +** Description This function is used to access the MTU in CONNECT packets +** +** Returns void. +** +*******************************************************************************/ +void obx_read_mtu(BT_HDR *p_pkt, tOBX_HANDLE handle, tOBX_CONN_EVT *p_evt) +{ + UINT8 *p = (UINT8 *)(p_pkt + 1) + p_pkt->offset + OBX_CONNECT_MTU_OFFSET; + + BE_STREAM_TO_UINT16(p_evt->mtu, p); + if (p_evt->mtu > OBX_MAX_MTU) + p_evt->mtu = OBX_MAX_MTU; + if (p_evt->mtu == 0) + p_evt->mtu = OBX_MIN_MTU; + + /* Get the Bd_Addr */ + OBX_GetPeerAddr (handle, p_evt->peer_addr); +} + +/******************************************************************************* +** +** Function obx_free_buf +** +** Description This function is used to free the GKI buffers of the given +** lower layer control block +** +** Returns void. +** +*******************************************************************************/ +void obx_free_buf(tOBX_LL_CB *p_ll_cb) +{ + void *p_pkt; + + + if (p_ll_cb->comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + { + if (p_ll_cb->port.p_rxmsg) + { + OBX_TRACE_WARNING0("obx_free_buf release p_rxmsg"); + GKI_freebuf(p_ll_cb->port.p_rxmsg); + p_ll_cb->port.p_rxmsg = 0; + } + } + + if (!GKI_queue_is_empty(&p_ll_cb->comm.rx_q)) + { + while((p_pkt = GKI_dequeue (&p_ll_cb->comm.rx_q)) != NULL) + { + OBX_TRACE_WARNING0("obx_free_buf release rx_q"); + GKI_freebuf(p_pkt); + } + } + + if (p_ll_cb->comm.p_txmsg) + { + OBX_TRACE_WARNING0("obx_free_buf release p_txmsg"); + GKI_freebuf(p_ll_cb->comm.p_txmsg); + p_ll_cb->comm.p_txmsg = 0; + } +} + +/******************************************************************************* +** +** Function obx_flow_control +** +** Description If we had flowed control the peer, enable the data path now +** +** Returns void. +** +*******************************************************************************/ +void obx_flow_control(tOBX_COMM_CB *p_comm) +{ + OBX_TRACE_DEBUG1 ("obx_flow_control stopped:%d", p_comm->stopped ); + if (p_comm->stopped) + { + if (p_comm->p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + { + PORT_FlowControl(p_comm->id, TRUE); + } + else + { + L2CA_FlowControl(p_comm->id, TRUE); + } + p_comm->stopped = FALSE; + } +} + +/***************************************************************************** +* Function: obx_read_triplet +* Purpose: Read the application parameters with the given tag +*****************************************************************************/ +UINT8 obx_read_triplet(tOBX_TRIPLET *p_trip, UINT8 num_trip, UINT8 tag) +{ + UINT8 xx = 0; + + for (xx=0; xx<num_trip; xx++, p_trip++) + { + if (p_trip->tag == tag) + { + break; + } + } + + return xx; +} +/***************************************************************************** +* Function: obx_read_obj_offset +* Purpose: Read the application parameters with the object offset tag +*****************************************************************************/ +UINT32 obx_read_obj_offset(tOBX_TRIPLET *p_trip, UINT8 num_trip) +{ + UINT32 obj_offset = 0; + UINT8 ind; + UINT8 *p = NULL, *pe; + UINT8 extra = 0, xx; + + ind = obx_read_triplet(p_trip, num_trip, OBX_TAG_SESS_PARAM_OBJ_OFF); + if (ind != num_trip) + { + if (p_trip[ind].len == 4) + { + p = p_trip[ind].p_array; + } + else if (p_trip[ind].len > 4) + { + extra = p_trip[ind].len - 4; + } + + if (extra) + { + /* the TLV is bigger than 4 bytes + * if the MSBs are all 0, we can still handle it with UINT32 */ + pe = p_trip[ind].p_array; + for (xx=0; xx<extra; xx++) + { + if (*pe == 0) + pe++; + else + break; + } + if (xx == extra) + p = pe; + } + + if (p) + BE_STREAM_TO_UINT32(obj_offset, p); + } + + return obj_offset; +} + +/******************************************************************************* +** +** Function obxu_dump_hex +** +** Description This function dumps hex data +** +*/ +#if (BT_USE_TRACES == TRUE) +void obxu_dump_hex (UINT8 *p, char *p_title, UINT16 len) +{ + UINT16 xx, yy; + char buff1[100], buff2[20]; + + if (p_title) + OBX_TRACE_DEBUG1 ("%s:", p_title); + + memset (buff2, ' ', 16); + buff2[16] = 0; + + yy = sprintf (buff1, "%04x: ", 0); + for (xx = 0; xx < len; xx++) + { + if ( (xx) && ((xx & 15) == 0) ) + { + OBX_TRACE_DEBUG2 (" %s %s", buff1, buff2); + yy = sprintf(buff1, "%04x: ", xx); + memset (buff2, ' ', 16); + } + yy += sprintf (&buff1[yy], "%02x ", *p); + + if ((*p >= ' ') && (*p <= 'z')) + buff2[xx & 15] = *p; + else + buff2[xx & 15] = '.'; + + p++; + } + + /* Pad out the remainder */ + for ( ; ; xx++) + { + if ((xx & 15) == 0) + { + OBX_TRACE_DEBUG2 (" %s %s", buff1, buff2); + break; + } + yy += sprintf (&buff1[yy], " "); + } + +} +#endif + +/******************************************************************************* +** +** Function OBX_GetPeerAddr +** +** Description This function is called to learn the Bluetooth address of the +** connected device. +** +** Returns L2CAP channel ID. +** +*******************************************************************************/ +UINT16 OBX_GetPeerAddr(tOBX_HANDLE shandle, BD_ADDR bd_addr) +{ + UINT16 lcid = 0; +#if (OBX_SERVER_INCLUDED == TRUE) + tOBX_SR_SESS_CB *p_scb; +#endif +#if (OBX_CLIENT_INCLUDED == TRUE) + tOBX_CL_CB *p_cb; + + if (shandle & OBX_CL_HANDLE_MASK) + { + p_cb = obx_cl_get_cb(shandle); + if (p_cb) + { + if (p_cb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + { + PORT_CheckConnection(p_cb->ll_cb.port.port_handle, p_cb->peer_addr, &lcid); + memcpy (bd_addr, p_cb->peer_addr, BD_ADDR_LEN); + lcid = p_cb->ll_cb.comm.id; + } + else if (p_cb->ll_cb.comm.id) + { + /* GetPeerAddr for l2c */ + memcpy (bd_addr, p_cb->peer_addr, BD_ADDR_LEN); + lcid = p_cb->ll_cb.comm.id; + } + } + } +#endif + +#if (OBX_SERVER_INCLUDED == TRUE) + if ((shandle & OBX_CL_HANDLE_MASK) == 0) + { + p_scb = obx_sr_get_scb(shandle); + if (p_scb) + { + if (p_scb->ll_cb.comm.p_send_fn == (tOBX_SEND_FN *)obx_rfc_snd_msg) + { + PORT_CheckConnection(p_scb->ll_cb.port.port_handle, bd_addr, &lcid); + } + else if (p_scb->ll_cb.comm.id) + { + /* GetPeerAddr for l2c */ + memcpy (bd_addr, p_scb->peer_addr, BD_ADDR_LEN); + lcid = p_scb->ll_cb.comm.id; + } + } + } +#endif + + return lcid; +} |