| /****************************************************************************** |
| * |
| * Copyright 2002-2012 Broadcom Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at: |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| ******************************************************************************/ |
| |
| /****************************************************************************** |
| * |
| * This module contains functions for parsing and building AVDTP signaling |
| * messages. It also contains functions called by the SCB or CCB state |
| * machines for sending command, response, and reject messages. It also |
| * contains a function that processes incoming messages and dispatches them |
| * to the appropriate SCB or CCB. |
| * |
| ******************************************************************************/ |
| |
| #include <string.h> |
| #include "avdt_api.h" |
| #include "avdt_int.h" |
| #include "avdtc_api.h" |
| #include "bt_common.h" |
| #include "bt_target.h" |
| #include "bt_types.h" |
| #include "bt_utils.h" |
| #include "btu.h" |
| #include "osi/include/osi.h" |
| |
| /***************************************************************************** |
| * constants |
| ****************************************************************************/ |
| |
| /* mask of all psc values */ |
| #define AVDT_MSG_PSC_MASK \ |
| (AVDT_PSC_TRANS | AVDT_PSC_REPORT | AVDT_PSC_DELAY_RPT | AVDT_PSC_RECOV | \ |
| AVDT_PSC_HDRCMP | AVDT_PSC_MUX) |
| #define AVDT_PSC_PROTECT (1 << 4) /* Content Protection */ |
| #define AVDT_PSC_CODEC (1 << 7) /* codec */ |
| |
| /***************************************************************************** |
| * type definitions |
| ****************************************************************************/ |
| |
| /* type for message building functions */ |
| typedef void (*tAVDT_MSG_BLD)(uint8_t** p, tAVDT_MSG* p_msg); |
| |
| /* type for message parsing functions */ |
| typedef uint8_t (*tAVDT_MSG_PRS)(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len); |
| |
| /***************************************************************************** |
| * local function declarations |
| ****************************************************************************/ |
| |
| static void avdt_msg_bld_none(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_single(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_setconfig_cmd(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_reconfig_cmd(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_multi(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_security_cmd(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_discover_rsp(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_svccap(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_security_rsp(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_all_svccap(uint8_t** p, tAVDT_MSG* p_msg); |
| static void avdt_msg_bld_delay_rpt(uint8_t** p, tAVDT_MSG* p_msg); |
| |
| static uint8_t avdt_msg_prs_none(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len); |
| static uint8_t avdt_msg_prs_single(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len); |
| static uint8_t avdt_msg_prs_setconfig_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_reconfig_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_multi(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len); |
| static uint8_t avdt_msg_prs_security_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_discover_rsp(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_svccap(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len); |
| static uint8_t avdt_msg_prs_all_svccap(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_security_rsp(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| static uint8_t avdt_msg_prs_delay_rpt(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len); |
| |
| /***************************************************************************** |
| * constants |
| ****************************************************************************/ |
| |
| /* table of information element minimum lengths used for parsing */ |
| const uint8_t avdt_msg_ie_len_min[] = { |
| 0, /* unused */ |
| AVDT_LEN_TRANS_MIN, /* media transport */ |
| AVDT_LEN_REPORT_MIN, /* reporting */ |
| AVDT_LEN_RECOV_MIN, /* recovery */ |
| AVDT_LEN_PROTECT_MIN, /* content protection */ |
| AVDT_LEN_HDRCMP_MIN, /* header compression */ |
| AVDT_LEN_MUX_MIN, /* multiplexing */ |
| AVDT_LEN_CODEC_MIN, /* codec */ |
| AVDT_LEN_DELAY_RPT_MIN /* delay report */ |
| }; |
| |
| /* table of information element minimum lengths used for parsing */ |
| const uint8_t avdt_msg_ie_len_max[] = { |
| 0, /* unused */ |
| AVDT_LEN_TRANS_MAX, /* media transport */ |
| AVDT_LEN_REPORT_MAX, /* reporting */ |
| AVDT_LEN_RECOV_MAX, /* recovery */ |
| AVDT_LEN_PROTECT_MAX, /* content protection */ |
| AVDT_LEN_HDRCMP_MAX, /* header compression */ |
| AVDT_LEN_MUX_MAX, /* multiplexing */ |
| AVDT_LEN_CODEC_MAX, /* codec */ |
| AVDT_LEN_DELAY_RPT_MAX /* delay report */ |
| }; |
| |
| /* table of error codes used when decoding information elements */ |
| const uint8_t avdt_msg_ie_err[] = { |
| 0, /* unused */ |
| AVDT_ERR_MEDIA_TRANS, /* media transport */ |
| AVDT_ERR_LENGTH, /* reporting */ |
| AVDT_ERR_RECOV_FMT, /* recovery */ |
| AVDT_ERR_CP_FMT, /* content protection */ |
| AVDT_ERR_ROHC_FMT, /* header compression */ |
| AVDT_ERR_MUX_FMT, /* multiplexing */ |
| AVDT_ERR_SERVICE, /* codec */ |
| AVDT_ERR_SERVICE /* delay report ?? */ |
| }; |
| |
| /* table of packet type minimum lengths */ |
| static const uint8_t avdt_msg_pkt_type_len[] = { |
| AVDT_LEN_TYPE_SINGLE, AVDT_LEN_TYPE_START, AVDT_LEN_TYPE_CONT, |
| AVDT_LEN_TYPE_END}; |
| |
| /* function table for building command messages */ |
| const tAVDT_MSG_BLD avdt_msg_bld_cmd[] = { |
| avdt_msg_bld_none, /* discover */ |
| avdt_msg_bld_single, /* get capabilities */ |
| avdt_msg_bld_setconfig_cmd, /* set configuration */ |
| avdt_msg_bld_single, /* get configuration */ |
| avdt_msg_bld_reconfig_cmd, /* reconfigure */ |
| avdt_msg_bld_single, /* open */ |
| avdt_msg_bld_multi, /* start */ |
| avdt_msg_bld_single, /* close */ |
| avdt_msg_bld_multi, /* suspend */ |
| avdt_msg_bld_single, /* abort */ |
| avdt_msg_bld_security_cmd, /* security control */ |
| avdt_msg_bld_single, /* get all capabilities */ |
| avdt_msg_bld_delay_rpt /* delay report */ |
| }; |
| |
| /* function table for building response messages */ |
| const tAVDT_MSG_BLD avdt_msg_bld_rsp[] = { |
| avdt_msg_bld_discover_rsp, /* discover */ |
| avdt_msg_bld_svccap, /* get capabilities */ |
| avdt_msg_bld_none, /* set configuration */ |
| avdt_msg_bld_all_svccap, /* get configuration */ |
| avdt_msg_bld_none, /* reconfigure */ |
| avdt_msg_bld_none, /* open */ |
| avdt_msg_bld_none, /* start */ |
| avdt_msg_bld_none, /* close */ |
| avdt_msg_bld_none, /* suspend */ |
| avdt_msg_bld_none, /* abort */ |
| avdt_msg_bld_security_rsp, /* security control */ |
| avdt_msg_bld_all_svccap, /* get all capabilities */ |
| avdt_msg_bld_none /* delay report */ |
| }; |
| |
| /* function table for parsing command messages */ |
| const tAVDT_MSG_PRS avdt_msg_prs_cmd[] = { |
| avdt_msg_prs_none, /* discover */ |
| avdt_msg_prs_single, /* get capabilities */ |
| avdt_msg_prs_setconfig_cmd, /* set configuration */ |
| avdt_msg_prs_single, /* get configuration */ |
| avdt_msg_prs_reconfig_cmd, /* reconfigure */ |
| avdt_msg_prs_single, /* open */ |
| avdt_msg_prs_multi, /* start */ |
| avdt_msg_prs_single, /* close */ |
| avdt_msg_prs_multi, /* suspend */ |
| avdt_msg_prs_single, /* abort */ |
| avdt_msg_prs_security_cmd, /* security control */ |
| avdt_msg_prs_single, /* get all capabilities */ |
| avdt_msg_prs_delay_rpt /* delay report */ |
| }; |
| |
| /* function table for parsing response messages */ |
| const tAVDT_MSG_PRS avdt_msg_prs_rsp[] = { |
| avdt_msg_prs_discover_rsp, /* discover */ |
| avdt_msg_prs_svccap, /* get capabilities */ |
| avdt_msg_prs_none, /* set configuration */ |
| avdt_msg_prs_all_svccap, /* get configuration */ |
| avdt_msg_prs_none, /* reconfigure */ |
| avdt_msg_prs_none, /* open */ |
| avdt_msg_prs_none, /* start */ |
| avdt_msg_prs_none, /* close */ |
| avdt_msg_prs_none, /* suspend */ |
| avdt_msg_prs_none, /* abort */ |
| avdt_msg_prs_security_rsp, /* security control */ |
| avdt_msg_prs_all_svccap, /* get all capabilities */ |
| avdt_msg_prs_none /* delay report */ |
| }; |
| |
| /* command message-to-event lookup table */ |
| const uint8_t avdt_msg_cmd_2_evt[] = { |
| AVDT_CCB_MSG_DISCOVER_CMD_EVT + AVDT_CCB_MKR, /* discover */ |
| AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get capabilities */ |
| AVDT_SCB_MSG_SETCONFIG_CMD_EVT, /* set configuration */ |
| AVDT_SCB_MSG_GETCONFIG_CMD_EVT, /* get configuration */ |
| AVDT_SCB_MSG_RECONFIG_CMD_EVT, /* reconfigure */ |
| AVDT_SCB_MSG_OPEN_CMD_EVT, /* open */ |
| AVDT_CCB_MSG_START_CMD_EVT + AVDT_CCB_MKR, /* start */ |
| AVDT_SCB_MSG_CLOSE_CMD_EVT, /* close */ |
| AVDT_CCB_MSG_SUSPEND_CMD_EVT + AVDT_CCB_MKR, /* suspend */ |
| AVDT_SCB_MSG_ABORT_CMD_EVT, /* abort */ |
| AVDT_SCB_MSG_SECURITY_CMD_EVT, /* security control */ |
| AVDT_CCB_MSG_GETCAP_CMD_EVT + AVDT_CCB_MKR, /* get all capabilities */ |
| AVDT_SCB_MSG_DELAY_RPT_CMD_EVT /* delay report */ |
| }; |
| |
| /* response message-to-event lookup table */ |
| const uint8_t avdt_msg_rsp_2_evt[] = { |
| AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ |
| AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ |
| AVDT_SCB_MSG_SETCONFIG_RSP_EVT, /* set configuration */ |
| AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ |
| AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ |
| AVDT_SCB_MSG_OPEN_RSP_EVT, /* open */ |
| AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ |
| AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ |
| AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ |
| AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ |
| AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ |
| AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ |
| AVDT_SCB_MSG_DELAY_RPT_RSP_EVT /* delay report */ |
| }; |
| |
| /* reject message-to-event lookup table */ |
| const uint8_t avdt_msg_rej_2_evt[] = { |
| AVDT_CCB_MSG_DISCOVER_RSP_EVT + AVDT_CCB_MKR, /* discover */ |
| AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get capabilities */ |
| AVDT_SCB_MSG_SETCONFIG_REJ_EVT, /* set configuration */ |
| AVDT_SCB_MSG_GETCONFIG_RSP_EVT, /* get configuration */ |
| AVDT_SCB_MSG_RECONFIG_RSP_EVT, /* reconfigure */ |
| AVDT_SCB_MSG_OPEN_REJ_EVT, /* open */ |
| AVDT_CCB_MSG_START_RSP_EVT + AVDT_CCB_MKR, /* start */ |
| AVDT_SCB_MSG_CLOSE_RSP_EVT, /* close */ |
| AVDT_CCB_MSG_SUSPEND_RSP_EVT + AVDT_CCB_MKR, /* suspend */ |
| AVDT_SCB_MSG_ABORT_RSP_EVT, /* abort */ |
| AVDT_SCB_MSG_SECURITY_RSP_EVT, /* security control */ |
| AVDT_CCB_MSG_GETCAP_RSP_EVT + AVDT_CCB_MKR, /* get all capabilities */ |
| 0 /* delay report */ |
| }; |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_cfg |
| * |
| * Description This function builds the configuration parameters contained |
| * in a command or response message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_cfg(uint8_t** p, tAVDT_CFG* p_cfg) { |
| uint8_t len; |
| |
| /* for now, just build media transport, codec, and content protection, and |
| * multiplexing */ |
| |
| /* media transport */ |
| if (p_cfg->psc_mask & AVDT_PSC_TRANS) { |
| *(*p)++ = AVDT_CAT_TRANS; |
| *(*p)++ = 0; /* length */ |
| } |
| |
| #if (AVDT_REPORTING == TRUE) |
| /* reporting transport */ |
| if (p_cfg->psc_mask & AVDT_PSC_REPORT) { |
| *(*p)++ = AVDT_CAT_REPORT; |
| *(*p)++ = 0; /* length */ |
| } |
| #endif |
| |
| /* codec */ |
| if (p_cfg->num_codec != 0) { |
| *(*p)++ = AVDT_CAT_CODEC; |
| len = p_cfg->codec_info[0] + 1; |
| if (len > AVDT_CODEC_SIZE) len = AVDT_CODEC_SIZE; |
| |
| memcpy(*p, p_cfg->codec_info, len); |
| *p += len; |
| } |
| |
| /* content protection */ |
| if (p_cfg->num_protect != 0) { |
| *(*p)++ = AVDT_CAT_PROTECT; |
| len = p_cfg->protect_info[0] + 1; |
| if (len > AVDT_PROTECT_SIZE) len = AVDT_PROTECT_SIZE; |
| |
| memcpy(*p, p_cfg->protect_info, len); |
| *p += len; |
| } |
| |
| /* delay report */ |
| if (p_cfg->psc_mask & AVDT_PSC_DELAY_RPT) { |
| *(*p)++ = AVDT_CAT_DELAY_RPT; |
| *(*p)++ = 0; /* length */ |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_none |
| * |
| * Description This message building function builds an empty message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_none(UNUSED_ATTR uint8_t** p, |
| UNUSED_ATTR tAVDT_MSG* p_msg) { |
| return; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_single |
| * |
| * Description This message building function builds a message containing |
| * a single SEID. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_single(uint8_t** p, tAVDT_MSG* p_msg) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->single.seid); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_setconfig_cmd |
| * |
| * Description This message building function builds a set configuration |
| * command message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_setconfig_cmd(uint8_t** p, tAVDT_MSG* p_msg) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.hdr.seid); |
| AVDT_MSG_BLD_SEID(*p, p_msg->config_cmd.int_seid); |
| avdt_msg_bld_cfg(p, p_msg->config_cmd.p_cfg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_reconfig_cmd |
| * |
| * Description This message building function builds a reconfiguration |
| * command message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_reconfig_cmd(uint8_t** p, tAVDT_MSG* p_msg) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->reconfig_cmd.hdr.seid); |
| |
| /* force psc mask zero to build only codec and security */ |
| p_msg->reconfig_cmd.p_cfg->psc_mask = 0; |
| avdt_msg_bld_cfg(p, p_msg->reconfig_cmd.p_cfg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_multi |
| * |
| * Description This message building function builds a message containing |
| * multiple SEID's. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_multi(uint8_t** p, tAVDT_MSG* p_msg) { |
| int i; |
| |
| for (i = 0; i < p_msg->multi.num_seps; i++) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->multi.seid_list[i]); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_security_cmd |
| * |
| * Description This message building function builds a security |
| * command message. |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_security_cmd(uint8_t** p, tAVDT_MSG* p_msg) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->security_cmd.hdr.seid); |
| memcpy(*p, p_msg->security_cmd.p_data, p_msg->security_cmd.len); |
| *p += p_msg->security_cmd.len; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_delay_rpt |
| * |
| * Description This message building function builds a delay report |
| * command message. |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_delay_rpt(uint8_t** p, tAVDT_MSG* p_msg) { |
| AVDT_MSG_BLD_SEID(*p, p_msg->delay_rpt_cmd.hdr.seid); |
| UINT16_TO_BE_STREAM(*p, p_msg->delay_rpt_cmd.delay); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_discover_rsp |
| * |
| * Description This message building function builds a discover |
| * response message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_discover_rsp(uint8_t** p, tAVDT_MSG* p_msg) { |
| int i; |
| |
| for (i = 0; i < p_msg->discover_rsp.num_seps; i++) { |
| /* build discover rsp info */ |
| AVDT_MSG_BLD_DISC(*p, p_msg->discover_rsp.p_sep_info[i].seid, |
| p_msg->discover_rsp.p_sep_info[i].in_use, |
| p_msg->discover_rsp.p_sep_info[i].media_type, |
| p_msg->discover_rsp.p_sep_info[i].tsep); |
| } |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_svccap |
| * |
| * Description This message building function builds a message containing |
| * service capabilities parameters. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_svccap(uint8_t** p, tAVDT_MSG* p_msg) { |
| tAVDT_CFG cfg; |
| |
| /* make sure the delay report category is not reported */ |
| memcpy(&cfg, p_msg->svccap.p_cfg, sizeof(tAVDT_CFG)); |
| cfg.psc_mask &= ~AVDT_PSC_DELAY_RPT; |
| avdt_msg_bld_cfg(p, &cfg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_all_svccap |
| * |
| * Description This message building function builds a message containing |
| * service capabilities parameters. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_all_svccap(uint8_t** p, tAVDT_MSG* p_msg) { |
| avdt_msg_bld_cfg(p, p_msg->svccap.p_cfg); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_bld_security_rsp |
| * |
| * Description This message building function builds a security |
| * response message. |
| * |
| * |
| * Returns void. |
| * |
| ******************************************************************************/ |
| static void avdt_msg_bld_security_rsp(uint8_t** p, tAVDT_MSG* p_msg) { |
| memcpy(*p, p_msg->security_rsp.p_data, p_msg->security_rsp.len); |
| *p += p_msg->security_rsp.len; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_cfg |
| * |
| * Description This message parsing function parses the configuration |
| * parameters field of a message. |
| * |
| * |
| * Returns Error code or zero if no error, and element that failed |
| * in p_elem. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_cfg(tAVDT_CFG* p_cfg, uint8_t* p, uint16_t len, |
| uint8_t* p_elem, uint8_t sig_id) { |
| uint8_t* p_end; |
| uint8_t elem = 0; |
| uint8_t elem_len; |
| uint8_t tmp; |
| uint8_t err = 0; |
| uint8_t protect_offset = 0; |
| |
| if (!p_cfg) { |
| AVDT_TRACE_ERROR("not expecting this cfg"); |
| return AVDT_ERR_BAD_STATE; |
| } |
| |
| p_cfg->psc_mask = 0; |
| p_cfg->num_codec = 0; |
| p_cfg->num_protect = 0; |
| |
| /* while there is still data to parse */ |
| p_end = p + len; |
| while ((p < p_end) && (err == 0)) { |
| /* verify overall length */ |
| if ((p_end - p) < AVDT_LEN_CFG_MIN) { |
| err = AVDT_ERR_PAYLOAD; |
| break; |
| } |
| |
| /* get and verify info elem id, length */ |
| elem = *p++; |
| elem_len = *p++; |
| |
| if ((elem == 0) || (elem > AVDT_CAT_MAX_CUR)) { |
| /* this may not be really bad. |
| * It may be a service category that is too new for us. |
| * allow these to be parsed without reporting an error. |
| * If this is a "capability" (as in GetCapRsp & GetConfigRsp), this is |
| * filtered out. |
| * If this is a Configuration (as in SetConfigCmd & ReconfigCmd), |
| * this will be marked as an error in the caller of this function */ |
| if ((sig_id == AVDT_SIG_SETCONFIG) || (sig_id == AVDT_SIG_RECONFIG)) { |
| /* Cannot accept unknown category. */ |
| err = AVDT_ERR_CATEGORY; |
| break; |
| } else /* GETCAP or GET_ALLCAP */ |
| { |
| /* Skip unknown categories. */ |
| p += elem_len; |
| AVDT_TRACE_DEBUG("skipping unknown service category=%d len: %d", elem, |
| elem_len); |
| continue; |
| } |
| } |
| |
| if ((elem_len > avdt_msg_ie_len_max[elem]) || |
| (elem_len < avdt_msg_ie_len_min[elem])) { |
| err = avdt_msg_ie_err[elem]; |
| break; |
| } |
| |
| /* add element to psc mask, but mask out codec or protect */ |
| p_cfg->psc_mask |= (1 << elem); |
| AVDT_TRACE_DEBUG("elem=%d elem_len: %d psc_mask=0x%x", elem, elem_len, |
| p_cfg->psc_mask); |
| |
| /* parse individual information elements with additional parameters */ |
| switch (elem) { |
| case AVDT_CAT_RECOV: |
| p_cfg->recov_type = *p++; |
| p_cfg->recov_mrws = *p++; |
| p_cfg->recov_mnmp = *p++; |
| if (p_cfg->recov_type != AVDT_RECOV_RFC2733) { |
| err = AVDT_ERR_RECOV_TYPE; |
| } else if ((p_cfg->recov_mrws < AVDT_RECOV_MRWS_MIN) || |
| (p_cfg->recov_mrws > AVDT_RECOV_MRWS_MAX) || |
| (p_cfg->recov_mnmp < AVDT_RECOV_MNMP_MIN) || |
| (p_cfg->recov_mnmp > AVDT_RECOV_MNMP_MAX)) { |
| err = AVDT_ERR_RECOV_FMT; |
| } |
| break; |
| |
| case AVDT_CAT_PROTECT: |
| p_cfg->psc_mask &= ~AVDT_PSC_PROTECT; |
| if ((elem_len + protect_offset) < AVDT_PROTECT_SIZE) { |
| p_cfg->num_protect++; |
| p_cfg->protect_info[protect_offset] = elem_len; |
| protect_offset++; |
| memcpy(&p_cfg->protect_info[protect_offset], p, elem_len); |
| protect_offset += elem_len; |
| } |
| p += elem_len; |
| break; |
| |
| case AVDT_CAT_HDRCMP: |
| p_cfg->hdrcmp_mask = *p++; |
| break; |
| |
| case AVDT_CAT_CODEC: |
| p_cfg->psc_mask &= ~AVDT_PSC_CODEC; |
| tmp = elem_len; |
| if (elem_len >= AVDT_CODEC_SIZE) { |
| tmp = AVDT_CODEC_SIZE - 1; |
| } |
| p_cfg->num_codec++; |
| p_cfg->codec_info[0] = elem_len; |
| memcpy(&p_cfg->codec_info[1], p, tmp); |
| p += elem_len; |
| break; |
| |
| case AVDT_CAT_DELAY_RPT: |
| break; |
| |
| default: |
| p += elem_len; |
| break; |
| } /* switch */ |
| } /* while ! err, !end*/ |
| *p_elem = elem; |
| AVDT_TRACE_DEBUG("err=0x%x, elem:0x%x psc_mask=0x%x", err, elem, |
| p_cfg->psc_mask); |
| |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_none |
| * |
| * Description This message parsing function parses a message with no |
| * parameters. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_none(UNUSED_ATTR tAVDT_MSG* p_msg, |
| UNUSED_ATTR uint8_t* p, |
| UNUSED_ATTR uint16_t len) { |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_single |
| * |
| * Description This message parsing function parses a message with a |
| * single SEID. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_single(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len) { |
| uint8_t err = 0; |
| |
| /* verify len */ |
| if (len != AVDT_LEN_SINGLE) { |
| err = AVDT_ERR_LENGTH; |
| } else { |
| AVDT_MSG_PRS_SEID(p, p_msg->single.seid); |
| |
| if (avdt_scb_by_hdl(p_msg->single.seid) == NULL) { |
| err = AVDT_ERR_SEID; |
| } |
| } |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_setconfig_cmd |
| * |
| * Description This message parsing function parses a set configuration |
| * command message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_setconfig_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| uint8_t err = 0; |
| |
| p_msg->hdr.err_param = 0; |
| |
| /* verify len */ |
| if (len < AVDT_LEN_SETCONFIG_MIN) { |
| err = AVDT_ERR_LENGTH; |
| } else { |
| /* get seids */ |
| AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.hdr.seid); |
| if (avdt_scb_by_hdl(p_msg->config_cmd.hdr.seid) == NULL) { |
| err = AVDT_ERR_SEID; |
| } |
| |
| AVDT_MSG_PRS_SEID(p, p_msg->config_cmd.int_seid); |
| if ((p_msg->config_cmd.int_seid < AVDT_SEID_MIN) || |
| (p_msg->config_cmd.int_seid > AVDT_SEID_MAX)) { |
| err = AVDT_ERR_SEID; |
| } |
| } |
| |
| if (!err) { |
| /* parse configuration parameters */ |
| len -= 2; |
| err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, |
| &p_msg->hdr.err_param, AVDT_SIG_SETCONFIG); |
| |
| if (!err) { |
| /* verify protocol service capabilities are supported */ |
| if (((p_msg->config_cmd.p_cfg->psc_mask & (~AVDT_PSC)) != 0) || |
| (p_msg->config_cmd.p_cfg->num_codec == 0)) { |
| err = AVDT_ERR_INVALID_CAP; |
| } |
| } |
| } |
| |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_reconfig_cmd |
| * |
| * Description This message parsing function parses a reconfiguration |
| * command message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_reconfig_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| uint8_t err = 0; |
| |
| p_msg->hdr.err_param = 0; |
| |
| /* verify len */ |
| if (len < AVDT_LEN_RECONFIG_MIN) { |
| err = AVDT_ERR_LENGTH; |
| } else { |
| /* get seid */ |
| AVDT_MSG_PRS_SEID(p, p_msg->reconfig_cmd.hdr.seid); |
| if (avdt_scb_by_hdl(p_msg->reconfig_cmd.hdr.seid) == NULL) { |
| err = AVDT_ERR_SEID; |
| } else { |
| /* parse config parameters */ |
| len--; |
| err = avdt_msg_prs_cfg(p_msg->config_cmd.p_cfg, p, len, |
| &p_msg->hdr.err_param, AVDT_SIG_RECONFIG); |
| |
| /* verify no protocol service capabilities in parameters */ |
| if (!err) { |
| AVDT_TRACE_DEBUG("avdt_msg_prs_reconfig_cmd psc_mask=0x%x/0x%x", |
| p_msg->config_cmd.p_cfg->psc_mask, AVDT_MSG_PSC_MASK); |
| if ((p_msg->config_cmd.p_cfg->psc_mask != 0) || |
| (p_msg->config_cmd.p_cfg->num_codec == 0 && |
| p_msg->config_cmd.p_cfg->num_protect == 0)) { |
| err = AVDT_ERR_INVALID_CAP; |
| } |
| } |
| } |
| } |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_multi |
| * |
| * Description This message parsing function parses a message containing |
| * multiple SEID's. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_multi(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len) { |
| int i; |
| uint8_t err = 0; |
| |
| p_msg->hdr.err_param = 0; |
| |
| /* verify len */ |
| if (len < AVDT_LEN_MULTI_MIN || (len > AVDT_NUM_SEPS)) { |
| err = AVDT_ERR_LENGTH; |
| } else { |
| /* get and verify all seps */ |
| for (i = 0; i < len; i++) { |
| AVDT_MSG_PRS_SEID(p, p_msg->multi.seid_list[i]); |
| if (avdt_scb_by_hdl(p_msg->multi.seid_list[i]) == NULL) { |
| err = AVDT_ERR_SEID; |
| p_msg->hdr.err_param = p_msg->multi.seid_list[i]; |
| break; |
| } |
| } |
| p_msg->multi.num_seps = (uint8_t)i; |
| } |
| |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_security_cmd |
| * |
| * Description This message parsing function parses a security |
| * command message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_security_cmd(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| uint8_t err = 0; |
| |
| /* verify len */ |
| if (len < AVDT_LEN_SECURITY_MIN) { |
| err = AVDT_ERR_LENGTH; |
| } else { |
| /* get seid */ |
| AVDT_MSG_PRS_SEID(p, p_msg->security_cmd.hdr.seid); |
| if (avdt_scb_by_hdl(p_msg->security_cmd.hdr.seid) == NULL) { |
| err = AVDT_ERR_SEID; |
| } else { |
| p_msg->security_cmd.p_data = p; |
| p_msg->security_cmd.len = len - 1; |
| } |
| } |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_discover_rsp |
| * |
| * Description This message parsing function parses a discover |
| * response message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_discover_rsp(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| int i; |
| uint8_t err = 0; |
| |
| /* determine number of seps; seps in msg is len/2, but set to minimum |
| ** of seps app has supplied memory for and seps in msg |
| */ |
| if (p_msg->discover_rsp.num_seps > (len / 2)) { |
| p_msg->discover_rsp.num_seps = (len / 2); |
| } |
| |
| /* parse out sep info */ |
| for (i = 0; i < p_msg->discover_rsp.num_seps; i++) { |
| /* parse discover rsp info */ |
| AVDT_MSG_PRS_DISC(p, p_msg->discover_rsp.p_sep_info[i].seid, |
| p_msg->discover_rsp.p_sep_info[i].in_use, |
| p_msg->discover_rsp.p_sep_info[i].media_type, |
| p_msg->discover_rsp.p_sep_info[i].tsep); |
| |
| /* verify that seid is valid */ |
| if ((p_msg->discover_rsp.p_sep_info[i].seid < AVDT_SEID_MIN) || |
| (p_msg->discover_rsp.p_sep_info[i].seid > AVDT_SEID_MAX)) { |
| err = AVDT_ERR_SEID; |
| break; |
| } |
| } |
| |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_svccap |
| * |
| * Description This message parsing function parses a message containing |
| * service capabilities parameters. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_svccap(tAVDT_MSG* p_msg, uint8_t* p, uint16_t len) { |
| /* parse parameters */ |
| uint8_t err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, |
| &p_msg->hdr.err_param, AVDT_SIG_GETCAP); |
| if (p_msg->svccap.p_cfg) { |
| p_msg->svccap.p_cfg->psc_mask &= AVDT_LEG_PSC; |
| } |
| |
| return (err); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_all_svccap |
| * |
| * Description This message parsing function parses a message containing |
| * service capabilities parameters. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_all_svccap(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| uint8_t err = avdt_msg_prs_cfg(p_msg->svccap.p_cfg, p, len, |
| &p_msg->hdr.err_param, AVDT_SIG_GET_ALLCAP); |
| if (p_msg->svccap.p_cfg) { |
| p_msg->svccap.p_cfg->psc_mask &= AVDT_MSG_PSC_MASK; |
| } |
| return (err); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_security_rsp |
| * |
| * Description This message parsing function parsing a security |
| * response message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_security_rsp(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| p_msg->security_rsp.p_data = p; |
| p_msg->security_rsp.len = len; |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_rej |
| * |
| * Description |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_rej(tAVDT_MSG* p_msg, uint8_t* p, uint8_t sig) { |
| if ((sig == AVDT_SIG_SETCONFIG) || (sig == AVDT_SIG_RECONFIG)) { |
| p_msg->hdr.err_param = *p++; |
| p_msg->hdr.err_code = *p; |
| } else if ((sig == AVDT_SIG_START) || (sig == AVDT_SIG_SUSPEND)) { |
| AVDT_MSG_PRS_SEID(p, p_msg->hdr.err_param); |
| p_msg->hdr.err_code = *p; |
| } else { |
| p_msg->hdr.err_code = *p; |
| } |
| |
| return 0; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_prs_delay_rpt |
| * |
| * Description This message parsing function parses a security |
| * command message. |
| * |
| * |
| * Returns Error code or zero if no error. |
| * |
| ******************************************************************************/ |
| static uint8_t avdt_msg_prs_delay_rpt(tAVDT_MSG* p_msg, uint8_t* p, |
| uint16_t len) { |
| uint8_t err = 0; |
| |
| /* verify len */ |
| if (len != AVDT_LEN_DELAY_RPT) { |
| AVDT_TRACE_WARNING("avdt_msg_prs_delay_rpt expected len: %u got: %u", |
| AVDT_LEN_DELAY_RPT, len); |
| err = AVDT_ERR_LENGTH; |
| } else { |
| /* get seid */ |
| AVDT_MSG_PRS_SEID(p, p_msg->delay_rpt_cmd.hdr.seid); |
| |
| if (avdt_scb_by_hdl(p_msg->delay_rpt_cmd.hdr.seid) == NULL) { |
| err = AVDT_ERR_SEID; |
| } else { |
| BE_STREAM_TO_UINT16(p_msg->delay_rpt_cmd.delay, p); |
| AVDT_TRACE_DEBUG("avdt_msg_prs_delay_rpt delay: %u", |
| p_msg->delay_rpt_cmd.delay); |
| } |
| } |
| return err; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_send |
| * |
| * Description Send, and if necessary fragment the next message. |
| * |
| * |
| * Returns Congested state; true if CCB congested, false if not. |
| * |
| ******************************************************************************/ |
| bool avdt_msg_send(tAVDT_CCB* p_ccb, BT_HDR* p_msg) { |
| uint16_t curr_msg_len; |
| uint8_t pkt_type; |
| uint8_t hdr_len; |
| tAVDT_TC_TBL* p_tbl; |
| BT_HDR* p_buf; |
| uint8_t* p; |
| uint8_t label; |
| uint8_t msg; |
| uint8_t sig; |
| uint8_t nosp = 0; /* number of subsequent packets */ |
| |
| /* look up transport channel table entry to get peer mtu */ |
| p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_SIG, p_ccb, NULL); |
| |
| /* set the current message if there is a message passed in */ |
| if (p_msg != NULL) { |
| p_ccb->p_curr_msg = p_msg; |
| } |
| |
| /* store copy of curr_msg->len */ |
| curr_msg_len = p_ccb->p_curr_msg->len; |
| |
| /* while not congested and we haven't sent it all */ |
| while ((!p_ccb->cong) && (p_ccb->p_curr_msg != NULL)) { |
| /* check what kind of message we've got here; we are using the offset |
| ** to indicate that a message is being fragmented |
| */ |
| |
| /* if message isn't being fragmented and it fits in mtu */ |
| if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && |
| (p_ccb->p_curr_msg->len <= p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) { |
| pkt_type = AVDT_PKT_TYPE_SINGLE; |
| hdr_len = AVDT_LEN_TYPE_SINGLE; |
| p_buf = p_ccb->p_curr_msg; |
| } |
| /* if message isn't being fragmented and it doesn't fit in mtu */ |
| else if ((p_ccb->p_curr_msg->offset == AVDT_MSG_OFFSET) && |
| (p_ccb->p_curr_msg->len > |
| p_tbl->peer_mtu - AVDT_LEN_TYPE_SINGLE)) { |
| pkt_type = AVDT_PKT_TYPE_START; |
| hdr_len = AVDT_LEN_TYPE_START; |
| nosp = (p_ccb->p_curr_msg->len + AVDT_LEN_TYPE_START - p_tbl->peer_mtu) / |
| (p_tbl->peer_mtu - 1) + |
| 2; |
| |
| /* get a new buffer for fragment we are sending */ |
| p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* copy portion of data from current message to new buffer */ |
| p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; |
| p_buf->len = p_tbl->peer_mtu - hdr_len; |
| memcpy((uint8_t*)(p_buf + 1) + p_buf->offset, |
| (uint8_t*)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, |
| p_buf->len); |
| } |
| /* if message is being fragmented and remaining bytes don't fit in mtu */ |
| else if ((p_ccb->p_curr_msg->offset > AVDT_MSG_OFFSET) && |
| (p_ccb->p_curr_msg->len > |
| (p_tbl->peer_mtu - AVDT_LEN_TYPE_CONT))) { |
| pkt_type = AVDT_PKT_TYPE_CONT; |
| hdr_len = AVDT_LEN_TYPE_CONT; |
| |
| /* get a new buffer for fragment we are sending */ |
| p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* copy portion of data from current message to new buffer */ |
| p_buf->offset = L2CAP_MIN_OFFSET + hdr_len; |
| p_buf->len = p_tbl->peer_mtu - hdr_len; |
| memcpy((uint8_t*)(p_buf + 1) + p_buf->offset, |
| (uint8_t*)(p_ccb->p_curr_msg + 1) + p_ccb->p_curr_msg->offset, |
| p_buf->len); |
| } |
| /* if message is being fragmented and remaining bytes do fit in mtu */ |
| else { |
| pkt_type = AVDT_PKT_TYPE_END; |
| hdr_len = AVDT_LEN_TYPE_END; |
| p_buf = p_ccb->p_curr_msg; |
| } |
| |
| /* label, sig id, msg type are in hdr of p_curr_msg */ |
| label = AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_msg->layer_specific); |
| msg = AVDT_LAYERSPEC_MSG(p_ccb->p_curr_msg->layer_specific); |
| sig = (uint8_t)p_ccb->p_curr_msg->event; |
| AVDT_TRACE_DEBUG("avdt_msg_send label:%d, msg:%d, sig:%d", label, msg, sig); |
| |
| /* keep track of how much of msg we've sent */ |
| curr_msg_len -= p_buf->len; |
| if (curr_msg_len == 0) { |
| /* entire message sent; mark as finished */ |
| p_ccb->p_curr_msg = NULL; |
| |
| /* start timer here for commands */ |
| if (msg == AVDT_MSG_TYPE_CMD) { |
| /* if retransmit timeout set to zero, sig doesn't use retransmit */ |
| if ((sig == AVDT_SIG_DISCOVER) || (sig == AVDT_SIG_GETCAP) || |
| (sig == AVDT_SIG_SECURITY) || (avdt_cb.rcb.ret_tout == 0)) { |
| alarm_cancel(p_ccb->idle_ccb_timer); |
| alarm_cancel(p_ccb->ret_ccb_timer); |
| period_ms_t interval_ms = avdt_cb.rcb.sig_tout * 1000; |
| alarm_set_on_mloop(p_ccb->rsp_ccb_timer, interval_ms, |
| avdt_ccb_rsp_ccb_timer_timeout, p_ccb); |
| } else if (sig != AVDT_SIG_DELAY_RPT) { |
| alarm_cancel(p_ccb->idle_ccb_timer); |
| alarm_cancel(p_ccb->rsp_ccb_timer); |
| period_ms_t interval_ms = avdt_cb.rcb.ret_tout * 1000; |
| alarm_set_on_mloop(p_ccb->ret_ccb_timer, interval_ms, |
| avdt_ccb_ret_ccb_timer_timeout, p_ccb); |
| } |
| } |
| } else { |
| /* message being fragmented and not completely sent */ |
| p_ccb->p_curr_msg->len -= p_buf->len; |
| p_ccb->p_curr_msg->offset += p_buf->len; |
| } |
| |
| /* set up to build header */ |
| p_buf->len += hdr_len; |
| p_buf->offset -= hdr_len; |
| p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* build header */ |
| AVDT_MSG_BLD_HDR(p, label, pkt_type, msg); |
| if (pkt_type == AVDT_PKT_TYPE_START) { |
| AVDT_MSG_BLD_NOSP(p, nosp); |
| } |
| if ((pkt_type == AVDT_PKT_TYPE_START) || |
| (pkt_type == AVDT_PKT_TYPE_SINGLE)) { |
| AVDT_MSG_BLD_SIG(p, sig); |
| } |
| |
| /* send msg buffer down */ |
| avdt_ad_write_req(AVDT_CHAN_SIG, p_ccb, NULL, p_buf); |
| } |
| return (p_ccb->cong); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_asmbl |
| * |
| * Description Reassemble incoming message. |
| * |
| * |
| * Returns Pointer to reassembled message; NULL if no message |
| * available. |
| * |
| ******************************************************************************/ |
| BT_HDR* avdt_msg_asmbl(tAVDT_CCB* p_ccb, BT_HDR* p_buf) { |
| uint8_t* p; |
| uint8_t pkt_type; |
| BT_HDR* p_ret; |
| |
| /* parse the message header */ |
| p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| AVDT_MSG_PRS_PKT_TYPE(p, pkt_type); |
| |
| /* quick sanity check on length */ |
| if (p_buf->len < avdt_msg_pkt_type_len[pkt_type]) { |
| osi_free(p_buf); |
| AVDT_TRACE_WARNING("Bad length during reassembly"); |
| p_ret = NULL; |
| } |
| /* single packet */ |
| else if (pkt_type == AVDT_PKT_TYPE_SINGLE) { |
| /* if reassembly in progress drop message and process new single */ |
| if (p_ccb->p_rx_msg != NULL) |
| AVDT_TRACE_WARNING("Got single during reassembly"); |
| |
| osi_free_and_reset((void**)&p_ccb->p_rx_msg); |
| |
| p_ret = p_buf; |
| } |
| /* start packet */ |
| else if (pkt_type == AVDT_PKT_TYPE_START) { |
| /* if reassembly in progress drop message and process new single */ |
| if (p_ccb->p_rx_msg != NULL) |
| AVDT_TRACE_WARNING("Got start during reassembly"); |
| |
| osi_free_and_reset((void**)&p_ccb->p_rx_msg); |
| |
| /* |
| * Allocate bigger buffer for reassembly. As lower layers are |
| * not aware of possible packet size after reassembly, they |
| * would have allocated smaller buffer. |
| */ |
| p_ccb->p_rx_msg = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE); |
| memcpy(p_ccb->p_rx_msg, p_buf, sizeof(BT_HDR) + p_buf->offset + p_buf->len); |
| |
| /* Free original buffer */ |
| osi_free(p_buf); |
| |
| /* update p to point to new buffer */ |
| p = (uint8_t*)(p_ccb->p_rx_msg + 1) + p_ccb->p_rx_msg->offset; |
| |
| /* copy first header byte over nosp */ |
| *(p + 1) = *p; |
| |
| /* set offset to point to where to copy next */ |
| p_ccb->p_rx_msg->offset += p_ccb->p_rx_msg->len; |
| |
| /* adjust length for packet header */ |
| p_ccb->p_rx_msg->len -= 1; |
| |
| p_ret = NULL; |
| } |
| /* continue or end */ |
| else { |
| /* if no reassembly in progress drop message */ |
| if (p_ccb->p_rx_msg == NULL) { |
| osi_free(p_buf); |
| AVDT_TRACE_WARNING("Pkt type=%d out of order", pkt_type); |
| p_ret = NULL; |
| } else { |
| /* get size of buffer holding assembled message */ |
| /* |
| * NOTE: The buffer is allocated above at the beginning of the |
| * reassembly, and is always of size BT_DEFAULT_BUFFER_SIZE. |
| */ |
| uint16_t buf_len = BT_DEFAULT_BUFFER_SIZE - sizeof(BT_HDR); |
| |
| /* adjust offset and len of fragment for header byte */ |
| p_buf->offset += AVDT_LEN_TYPE_CONT; |
| p_buf->len -= AVDT_LEN_TYPE_CONT; |
| |
| /* verify length */ |
| if ((p_ccb->p_rx_msg->offset + p_buf->len) > buf_len) { |
| /* won't fit; free everything */ |
| AVDT_TRACE_WARNING("%s: Fragmented message too big!", __func__); |
| osi_free_and_reset((void**)&p_ccb->p_rx_msg); |
| osi_free(p_buf); |
| p_ret = NULL; |
| } else { |
| /* copy contents of p_buf to p_rx_msg */ |
| memcpy((uint8_t*)(p_ccb->p_rx_msg + 1) + p_ccb->p_rx_msg->offset, |
| (uint8_t*)(p_buf + 1) + p_buf->offset, p_buf->len); |
| |
| if (pkt_type == AVDT_PKT_TYPE_END) { |
| p_ccb->p_rx_msg->offset -= p_ccb->p_rx_msg->len; |
| p_ccb->p_rx_msg->len += p_buf->len; |
| p_ret = p_ccb->p_rx_msg; |
| p_ccb->p_rx_msg = NULL; |
| } else { |
| p_ccb->p_rx_msg->offset += p_buf->len; |
| p_ccb->p_rx_msg->len += p_buf->len; |
| p_ret = NULL; |
| } |
| osi_free(p_buf); |
| } |
| } |
| } |
| return p_ret; |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_send_cmd |
| * |
| * Description This function is called to send a command message. The |
| * sig_id parameter indicates the message type, p_params |
| * points to the message parameters, if any. It gets a buffer |
| * from the AVDTP command pool, executes the message building |
| * function for this message type. It then queues the message |
| * in the command queue for this CCB. |
| * |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_msg_send_cmd(tAVDT_CCB* p_ccb, void* p_scb, uint8_t sig_id, |
| tAVDT_MSG* p_params) { |
| uint8_t* p; |
| uint8_t* p_start; |
| BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* set up buf pointer and offset */ |
| p_buf->offset = AVDT_MSG_OFFSET; |
| p_start = p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* execute parameter building function to build message */ |
| (*avdt_msg_bld_cmd[sig_id - 1])(&p, p_params); |
| |
| /* set len */ |
| p_buf->len = (uint16_t)(p - p_start); |
| |
| /* now store scb hdls, if any, in buf */ |
| if (p_scb != NULL) { |
| p = (uint8_t*)(p_buf + 1); |
| |
| /* for start and suspend, p_scb points to array of handles */ |
| if ((sig_id == AVDT_SIG_START) || (sig_id == AVDT_SIG_SUSPEND)) { |
| memcpy(p, (uint8_t*)p_scb, p_buf->len); |
| } |
| /* for all others, p_scb points to scb as usual */ |
| else { |
| *p = avdt_scb_to_hdl((tAVDT_SCB*)p_scb); |
| } |
| } |
| |
| /* stash sig, label, and message type in buf */ |
| p_buf->event = sig_id; |
| AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_CMD, p_ccb->label); |
| |
| /* increment label */ |
| p_ccb->label = (p_ccb->label + 1) % 16; |
| |
| /* queue message and trigger ccb to send it */ |
| fixed_queue_enqueue(p_ccb->cmd_q, p_buf); |
| avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_send_rsp |
| * |
| * Description This function is called to send a response message. The |
| * sig_id parameter indicates the message type, p_params |
| * points to the message parameters, if any. It gets a buffer |
| * from the AVDTP command pool, executes the message building |
| * function for this message type. It then queues the message |
| * in the response queue for this CCB. |
| * |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_msg_send_rsp(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) { |
| uint8_t* p; |
| uint8_t* p_start; |
| BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* set up buf pointer and offset */ |
| p_buf->offset = AVDT_MSG_OFFSET; |
| p_start = p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* execute parameter building function to build message */ |
| (*avdt_msg_bld_rsp[sig_id - 1])(&p, p_params); |
| |
| /* set length */ |
| p_buf->len = (uint16_t)(p - p_start); |
| |
| /* stash sig, label, and message type in buf */ |
| p_buf->event = sig_id; |
| AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_RSP, |
| p_params->hdr.label); |
| |
| /* queue message and trigger ccb to send it */ |
| fixed_queue_enqueue(p_ccb->rsp_q, p_buf); |
| avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_send_rej |
| * |
| * Description This function is called to send a reject message. The |
| * sig_id parameter indicates the message type. It gets |
| * a buffer from the AVDTP command pool and builds the |
| * message based on the message type and the error code. |
| * It then queues the message in the response queue for |
| * this CCB. |
| * |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_msg_send_rej(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) { |
| uint8_t* p; |
| uint8_t* p_start; |
| BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* set up buf pointer and offset */ |
| p_buf->offset = AVDT_MSG_OFFSET; |
| p_start = p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* if sig id included, build into message */ |
| if (sig_id != AVDT_SIG_NONE) { |
| /* if this sig has a parameter, add the parameter */ |
| if ((sig_id == AVDT_SIG_SETCONFIG) || (sig_id == AVDT_SIG_RECONFIG)) { |
| AVDT_MSG_BLD_PARAM(p, p_params->hdr.err_param); |
| } else if ((sig_id == AVDT_SIG_START) || (sig_id == AVDT_SIG_SUSPEND)) { |
| AVDT_MSG_BLD_SEID(p, p_params->hdr.err_param); |
| } |
| |
| /* add the error code */ |
| AVDT_MSG_BLD_ERR(p, p_params->hdr.err_code); |
| } |
| AVDT_TRACE_DEBUG("avdt_msg_send_rej"); |
| |
| /* calculate length */ |
| p_buf->len = (uint16_t)(p - p_start); |
| |
| /* stash sig, label, and message type in buf */ |
| p_buf->event = sig_id; |
| AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_REJ, |
| p_params->hdr.label); |
| |
| /* queue message and trigger ccb to send it */ |
| fixed_queue_enqueue(p_ccb->rsp_q, p_buf); |
| avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_send_grej |
| * |
| * Description This function is called to send a general reject message. |
| * The sig_id parameter indicates the message type. It gets |
| * a buffer from the AVDTP command pool and builds the |
| * message based on the message type and the error code. |
| * It then queues the message in the response queue for |
| * this CCB. |
| * |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_msg_send_grej(tAVDT_CCB* p_ccb, uint8_t sig_id, tAVDT_MSG* p_params) { |
| uint8_t* p; |
| uint8_t* p_start; |
| BT_HDR* p_buf = (BT_HDR*)osi_malloc(AVDT_CMD_BUF_SIZE); |
| |
| /* set up buf pointer and offset */ |
| p_buf->offset = AVDT_MSG_OFFSET; |
| p_start = p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* calculate length */ |
| p_buf->len = (uint16_t)(p - p_start); |
| |
| /* stash sig, label, and message type in buf */ |
| p_buf->event = sig_id; |
| AVDT_BLD_LAYERSPEC(p_buf->layer_specific, AVDT_MSG_TYPE_GRJ, |
| p_params->hdr.label); |
| AVDT_TRACE_DEBUG(__func__); |
| |
| /* queue message and trigger ccb to send it */ |
| fixed_queue_enqueue(p_ccb->rsp_q, p_buf); |
| avdt_ccb_event(p_ccb, AVDT_CCB_SENDMSG_EVT, NULL); |
| } |
| |
| /******************************************************************************* |
| * |
| * Function avdt_msg_ind |
| * |
| * Description This function is called by the adaption layer when an |
| * incoming message is received on the signaling channel. |
| * It parses the message and sends an event to the appropriate |
| * SCB or CCB for the message. |
| * |
| * |
| * Returns Nothing. |
| * |
| ******************************************************************************/ |
| void avdt_msg_ind(tAVDT_CCB* p_ccb, BT_HDR* p_buf) { |
| tAVDT_SCB* p_scb; |
| uint8_t* p; |
| bool ok = true; |
| bool handle_rsp = false; |
| bool gen_rej = false; |
| uint8_t label; |
| uint8_t pkt_type; |
| uint8_t msg_type; |
| uint8_t sig = 0; |
| tAVDT_MSG msg; |
| tAVDT_CFG cfg; |
| uint8_t err; |
| uint8_t evt = 0; |
| uint8_t scb_hdl; |
| |
| /* reassemble message; if no message available (we received a fragment) return |
| */ |
| p_buf = avdt_msg_asmbl(p_ccb, p_buf); |
| if (p_buf == NULL) { |
| return; |
| } |
| |
| p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
| |
| /* parse the message header */ |
| AVDT_MSG_PRS_HDR(p, label, pkt_type, msg_type); |
| |
| AVDT_TRACE_DEBUG("msg_type=%d, sig=%d", msg_type, sig); |
| /* set up label and ccb_idx in message hdr */ |
| msg.hdr.label = label; |
| msg.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb); |
| |
| /* verify msg type */ |
| if (msg_type == AVDT_MSG_TYPE_GRJ) { |
| AVDT_TRACE_WARNING("Dropping msg msg_type=%d", msg_type); |
| ok = false; |
| } |
| /* check for general reject */ |
| else if ((msg_type == AVDT_MSG_TYPE_REJ) && |
| (p_buf->len == AVDT_LEN_GEN_REJ)) { |
| gen_rej = true; |
| if (p_ccb->p_curr_cmd != NULL) { |
| msg.hdr.sig_id = sig = (uint8_t)p_ccb->p_curr_cmd->event; |
| evt = avdt_msg_rej_2_evt[sig - 1]; |
| msg.hdr.err_code = AVDT_ERR_NSC; |
| msg.hdr.err_param = 0; |
| } |
| } else /* not a general reject */ |
| { |
| /* get and verify signal */ |
| AVDT_MSG_PRS_SIG(p, sig); |
| msg.hdr.sig_id = sig; |
| if ((sig == 0) || (sig > AVDT_SIG_MAX)) { |
| AVDT_TRACE_WARNING("Dropping msg sig=%d msg_type:%d", sig, msg_type); |
| ok = false; |
| |
| /* send a general reject */ |
| if (msg_type == AVDT_MSG_TYPE_CMD) { |
| avdt_msg_send_grej(p_ccb, sig, &msg); |
| } |
| } |
| } |
| |
| if (ok && !gen_rej) { |
| /* skip over header (msg length already verified during reassembly) */ |
| p_buf->len -= AVDT_LEN_TYPE_SINGLE; |
| |
| /* set up to parse message */ |
| if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_DISCOVER)) { |
| /* parse discover rsp message to struct supplied by app */ |
| msg.discover_rsp.p_sep_info = (tAVDT_SEP_INFO*)p_ccb->p_proc_data; |
| msg.discover_rsp.num_seps = p_ccb->proc_param; |
| } else if ((msg_type == AVDT_MSG_TYPE_RSP) && |
| ((sig == AVDT_SIG_GETCAP) || (sig == AVDT_SIG_GET_ALLCAP))) { |
| /* parse discover rsp message to struct supplied by app */ |
| msg.svccap.p_cfg = (tAVDT_CFG*)p_ccb->p_proc_data; |
| } else if ((msg_type == AVDT_MSG_TYPE_RSP) && (sig == AVDT_SIG_GETCONFIG)) { |
| /* parse get config rsp message to struct allocated locally */ |
| msg.svccap.p_cfg = &cfg; |
| } else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_SETCONFIG)) { |
| /* parse config cmd message to struct allocated locally */ |
| msg.config_cmd.p_cfg = &cfg; |
| } else if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig == AVDT_SIG_RECONFIG)) { |
| /* parse reconfig cmd message to struct allocated locally */ |
| msg.reconfig_cmd.p_cfg = &cfg; |
| } |
| |
| /* parse message; while we're at it map message sig to event */ |
| if (msg_type == AVDT_MSG_TYPE_CMD) { |
| msg.hdr.err_code = err = |
| (*avdt_msg_prs_cmd[sig - 1])(&msg, p, p_buf->len); |
| evt = avdt_msg_cmd_2_evt[sig - 1]; |
| } else if (msg_type == AVDT_MSG_TYPE_RSP) { |
| msg.hdr.err_code = err = |
| (*avdt_msg_prs_rsp[sig - 1])(&msg, p, p_buf->len); |
| evt = avdt_msg_rsp_2_evt[sig - 1]; |
| } else /* msg_type == AVDT_MSG_TYPE_REJ */ |
| { |
| err = avdt_msg_prs_rej(&msg, p, sig); |
| evt = avdt_msg_rej_2_evt[sig - 1]; |
| } |
| |
| /* if parsing failed */ |
| if (err != 0) { |
| AVDT_TRACE_WARNING("Parsing failed sig=%d err=0x%x", sig, err); |
| |
| /* if its a rsp or rej, drop it; if its a cmd, send a rej; |
| ** note special case for abort; never send abort reject |
| */ |
| ok = false; |
| if ((msg_type == AVDT_MSG_TYPE_CMD) && (sig != AVDT_SIG_ABORT)) { |
| avdt_msg_send_rej(p_ccb, sig, &msg); |
| } |
| } |
| } |
| |
| /* if its a rsp or rej, check sent cmd to see if we're waiting for |
| ** the rsp or rej. If we didn't send a cmd for it, drop it. If |
| ** it does match a cmd, stop timer for the cmd. |
| */ |
| if (ok) { |
| if ((msg_type == AVDT_MSG_TYPE_RSP) || (msg_type == AVDT_MSG_TYPE_REJ)) { |
| if ((p_ccb->p_curr_cmd != NULL) && (p_ccb->p_curr_cmd->event == sig) && |
| (AVDT_LAYERSPEC_LABEL(p_ccb->p_curr_cmd->layer_specific) == label)) { |
| /* stop timer */ |
| alarm_cancel(p_ccb->idle_ccb_timer); |
| alarm_cancel(p_ccb->ret_ccb_timer); |
| alarm_cancel(p_ccb->rsp_ccb_timer); |
| |
| /* clear retransmission count */ |
| p_ccb->ret_count = 0; |
| |
| /* later in this function handle ccb event */ |
| handle_rsp = true; |
| } else { |
| ok = false; |
| AVDT_TRACE_WARNING("Cmd not found for rsp sig=%d label=%d", sig, label); |
| } |
| } |
| } |
| |
| if (ok) { |
| /* if it's a ccb event send to ccb */ |
| if (evt & AVDT_CCB_MKR) { |
| tAVDT_CCB_EVT avdt_ccb_evt; |
| avdt_ccb_evt.msg = msg; |
| avdt_ccb_event(p_ccb, (uint8_t)(evt & ~AVDT_CCB_MKR), &avdt_ccb_evt); |
| } |
| /* if it's a scb event */ |
| else { |
| /* Scb events always have a single seid. For cmd, get seid from |
| ** message. For rej and rsp, get seid from p_curr_cmd. |
| */ |
| if (msg_type == AVDT_MSG_TYPE_CMD) { |
| scb_hdl = msg.single.seid; |
| } else { |
| scb_hdl = *((uint8_t*)(p_ccb->p_curr_cmd + 1)); |
| } |
| |
| /* Map seid to the scb and send it the event. For cmd, seid has |
| ** already been verified by parsing function. |
| */ |
| if (evt) { |
| p_scb = avdt_scb_by_hdl(scb_hdl); |
| if (p_scb != NULL) { |
| tAVDT_SCB_EVT avdt_scb_evt; |
| avdt_scb_evt.msg = msg; |
| avdt_scb_event(p_scb, evt, &avdt_scb_evt); |
| } |
| } |
| } |
| } |
| |
| /* free message buffer */ |
| osi_free(p_buf); |
| |
| /* if its a rsp or rej, send event to ccb to free associated |
| ** cmd msg buffer and handle cmd queue |
| */ |
| if (handle_rsp) { |
| avdt_ccb_event(p_ccb, AVDT_CCB_RCVRSP_EVT, NULL); |
| } |
| } |