1285 lines
48 KiB
C
1285 lines
48 KiB
C
|
#include <stdint.h>
|
|||
|
#include <stddef.h>
|
|||
|
#include <string.h>
|
|||
|
#include <assert.h>
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
#include "btdm_host.h"
|
|||
|
#include "fdb_app.h"
|
|||
|
|
|||
|
#include "app_config.h"
|
|||
|
#include "app_bt.h"
|
|||
|
#include "user_bt.h"
|
|||
|
|
|||
|
#ifndef LOG_ENABLE
|
|||
|
#define LOG_ENABLE
|
|||
|
#endif
|
|||
|
#include "co_log.h"
|
|||
|
#ifdef LOG_LOCAL_LEVEL
|
|||
|
#undef LOG_LOCAL_LEVEL
|
|||
|
#endif
|
|||
|
#define LOG_LOCAL_LEVEL LOG_LEVEL_INFO
|
|||
|
|
|||
|
#define NUM_STREAMS 4
|
|||
|
|
|||
|
#define max(a, b) ((a) > (b) ? (a) : (b))
|
|||
|
#define min(a, b) ((a) > (b) ? (b) : (a))
|
|||
|
|
|||
|
const BtAccessModeInfo access_mode_nc = {
|
|||
|
.inqInterval = 0x800,
|
|||
|
.inqWindow = 0x12,
|
|||
|
.pageInterval = 0x800,
|
|||
|
.pageWindow = 0x12,
|
|||
|
};
|
|||
|
|
|||
|
static BtHandler securityHandler;
|
|||
|
static BtHandler globalHandler;
|
|||
|
static BtSecurityParms secParms;
|
|||
|
|
|||
|
static char hf_codec[2];
|
|||
|
uint8_t default_name[] = "FR30xx_0000";
|
|||
|
//HfChannel hf_channel[NUM_BT_DEVICES];
|
|||
|
//HfgChannel hfg_channel[NUM_BT_DEVICES];
|
|||
|
//A2dpStream Stream[NUM_STREAMS];
|
|||
|
//AvrcpChannel rcpCtChannel[NUM_BT_DEVICES];
|
|||
|
//AvrcpChannel rcpTgChannel[NUM_BT_DEVICES];
|
|||
|
|
|||
|
HfChannel *hf_channel;
|
|||
|
HfgChannel *hfg_channel;
|
|||
|
A2dpStream *Stream;
|
|||
|
AvrcpChannel *rcpCtChannel;
|
|||
|
AvrcpChannel *rcpTgChannel;
|
|||
|
PbapClientSession *pbap_client; /* PBAP client */
|
|||
|
SppDev *spp_dev;
|
|||
|
|
|||
|
//BtPacket *scoPacket;
|
|||
|
|
|||
|
uint8_t SbcSnkElements[]={
|
|||
|
A2DP_SBC_CODEC_INF_ELEMENT(
|
|||
|
/* 48000 and 44100 are required for SNK */
|
|||
|
A2DP_SBC_CODEC_FREQ_48000 |
|
|||
|
A2DP_SBC_CODEC_FREQ_44100 |
|
|||
|
A2DP_SBC_CODEC_FREQ_32000 |
|
|||
|
A2DP_SBC_CODEC_FREQ_16000,
|
|||
|
|
|||
|
/* All modes required for SNK */
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_MONO |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_DUAL |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_STEREO |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_JOINT,
|
|||
|
|
|||
|
/* One block size must be supported */
|
|||
|
A2DP_SBC_CODEC_BLOCKS_16 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_12 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_8 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_4,
|
|||
|
|
|||
|
/* Both 8 and 4 must be supported */
|
|||
|
A2DP_SBC_CODEC_SUBBANDS_8 |
|
|||
|
A2DP_SBC_CODEC_SUBBANDS_4,
|
|||
|
|
|||
|
/* Both allocation methods must be supported in SNK */
|
|||
|
A2DP_SBC_CODEC_ALLOCATION_LOUDNESS |
|
|||
|
A2DP_SBC_CODEC_ALLOCATION_SNR,
|
|||
|
|
|||
|
/* Minium bitpool */
|
|||
|
2,
|
|||
|
|
|||
|
/* Maximum bitpool */
|
|||
|
53)
|
|||
|
|
|||
|
};
|
|||
|
|
|||
|
uint8_t AacSnkElements[] = {
|
|||
|
A2DP_AAC_CODEC_MPEG2_LC |
|
|||
|
A2DP_AAC_CODEC_MPEG4_LC,
|
|||
|
|
|||
|
A2DP_AAC_CODEC_FREQ_44100,
|
|||
|
|
|||
|
A2DP_AAC_CODEC_FREQ_48000 |
|
|||
|
A2DP_AAC_CODEC_CHNL_MONO |
|
|||
|
A2DP_AAC_CODEC_CHNL_STEREO,
|
|||
|
|
|||
|
A2DP_AAC_CODEC_VBR,
|
|||
|
|
|||
|
0x00, 0x00
|
|||
|
};
|
|||
|
|
|||
|
/* Source SBC Elements */
|
|||
|
static uint8_t SbcSrcElements[] = {
|
|||
|
A2DP_SBC_CODEC_INF_ELEMENT(/* 48000 or 44100 is required for SRC */
|
|||
|
/*A2DP_SBC_CODEC_FREQ_48000 | */
|
|||
|
A2DP_SBC_CODEC_FREQ_44100 /*|
|
|||
|
A2DP_SBC_CODEC_FREQ_32000 |
|
|||
|
A2DP_SBC_CODEC_FREQ_16000*/,
|
|||
|
|
|||
|
/* MONO and one other required for SRC */
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_MONO |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_DUAL |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_STEREO |
|
|||
|
A2DP_SBC_CODEC_CHNL_MODE_JOINT,
|
|||
|
|
|||
|
/* One block size must be supported */
|
|||
|
A2DP_SBC_CODEC_BLOCKS_16 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_12 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_8 |
|
|||
|
A2DP_SBC_CODEC_BLOCKS_4,
|
|||
|
|
|||
|
/* SRC must support 8 subbands */
|
|||
|
A2DP_SBC_CODEC_SUBBANDS_8 |
|
|||
|
A2DP_SBC_CODEC_SUBBANDS_4,
|
|||
|
|
|||
|
/* SRC must support LOUDNESS */
|
|||
|
A2DP_SBC_CODEC_ALLOCATION_LOUDNESS |
|
|||
|
A2DP_SBC_CODEC_ALLOCATION_SNR,
|
|||
|
|
|||
|
/* Minium bitpool */
|
|||
|
2,
|
|||
|
|
|||
|
/* Maximum bitpool */
|
|||
|
53)
|
|||
|
};
|
|||
|
|
|||
|
AvdtpCodec sbcSnkCodec1; //local codec information
|
|||
|
AvdtpCodec aacSnkCodec1;
|
|||
|
AvdtpCodec sbcSnkCodec2; //local codec information
|
|||
|
AvdtpCodec aacSnkCodec2;
|
|||
|
|
|||
|
AvdtpCodec sbcSrcCodec1;
|
|||
|
AvdtpCodec sbcSrcCodec2;
|
|||
|
|
|||
|
static AvdtpCodec cfgCodec;
|
|||
|
static uint8_t cfgElements[4];
|
|||
|
|
|||
|
static app_btdm_callback_t btdm_cb = NULL;
|
|||
|
extern void CMGR_SetAudioVoiceSettings(BtScoAudioSettings settings);
|
|||
|
|
|||
|
uint8_t bt_get_free_hf_channel(void)
|
|||
|
{
|
|||
|
uint8_t index = 0;
|
|||
|
for(index = 0; index < NUM_BT_DEVICES; index++){
|
|||
|
if(hf_channel[index].state == HF_STATE_CLOSED){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return index;
|
|||
|
}
|
|||
|
|
|||
|
uint8_t bt_get_free_hfg_channel(void)
|
|||
|
{
|
|||
|
uint8_t index = 0;
|
|||
|
for(index = 0; index < NUM_BT_DEVICES; index++){
|
|||
|
if(hfg_channel[index].state == HFG_STATE_CLOSED){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return index;
|
|||
|
}
|
|||
|
|
|||
|
uint8_t bt_get_free_a2dp_stream(void)
|
|||
|
{
|
|||
|
uint8_t index = 0;
|
|||
|
for(index = 0; index < NUM_STREAMS; index++){
|
|||
|
//if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device->state == A2DP_DEV_STATE_DISCONNECTED)){
|
|||
|
if(Is_a2dp_stream_registered(&Stream[index]) && (Stream[index].device == NULL)){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return index;
|
|||
|
}
|
|||
|
|
|||
|
uint8_t bt_get_free_avrcp_channel(void)
|
|||
|
{
|
|||
|
uint8_t index = 0;
|
|||
|
for(index = 0; index < NUM_BT_DEVICES; index++){
|
|||
|
if(rcpCtChannel[index].chnl.conn.state == 0){
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
return index;
|
|||
|
}
|
|||
|
|
|||
|
static void security_event_handler(const BtEvent *event)
|
|||
|
{
|
|||
|
//BtDeviceContext *bdc;
|
|||
|
printf("secureity event handler %d\r\n",event->eType);
|
|||
|
switch(event->eType) {
|
|||
|
case BTEVENT_PIN_REQ:
|
|||
|
SEC_SetPin(event->p.remDev, (const uint8_t *)"0000", 4, BPT_SAVE_TRUSTED);
|
|||
|
break;
|
|||
|
case BTEVENT_AUTHORIZATION_REQ:
|
|||
|
SEC_Authorize(event->p.remDev, TRUE, TRUE);
|
|||
|
break;
|
|||
|
//case BTEVENT_PAIRING_COMPLETE:
|
|||
|
// bdc = DS_FindDevice(&event->p.pairingInfo.remDev->bdAddr);
|
|||
|
// break;
|
|||
|
default:
|
|||
|
//co_printf("SecurityEventHandler: event--0x%02x is not implemented.\r\n", event->eType);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void me_callback(const BtEvent *event)
|
|||
|
{
|
|||
|
LOG_INFO(NULL, "me_callback :%d,%d\r\n",event->eType,event->errCode);
|
|||
|
|
|||
|
switch (event->eType) {
|
|||
|
case BTEVENT_HCI_INITIALIZED:
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_INQUIRY_CANCELED:
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_INQUIRY_RESULT:
|
|||
|
printf("inq result: %x\r\n",event->p.inqResult.classOfDevice);
|
|||
|
printf("addr:0x%02x%02x%02x%02x%02x%02x\r\n",event->p.inqResult.bdAddr.A[0],event->p.inqResult.bdAddr.A[1],event->p.inqResult.bdAddr.A[2],
|
|||
|
event->p.inqResult.bdAddr.A[3],event->p.inqResult.bdAddr.A[4],event->p.inqResult.bdAddr.A[5]);
|
|||
|
//printf("rssi:%d\r\n",event->p.inqResult.rssi);
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_INQUIRY_COMPLETE:
|
|||
|
ME_Inquiry(BT_IAC_GIAC, 5, 5);
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_LINK_CONNECT_IND:
|
|||
|
// if(user_bt_env.bt_connect_cb){
|
|||
|
// user_bt_env.bt_connect_cb(BTEVENT_LINK_CONNECT_IND, &event->p.remDev->bdAddr, event->errCode);
|
|||
|
// }
|
|||
|
bt_connect_act_cmp(BT_EVENT_CON_IND, event->errCode, event->p.remDev);
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_LINK_CONNECT_CNF:
|
|||
|
// if(user_bt_env.bt_connect_cb){
|
|||
|
// user_bt_env.bt_connect_cb(BTEVENT_LINK_CONNECT_CNF, &event->p.remDev->bdAddr, event->errCode);
|
|||
|
// }
|
|||
|
bt_connect_act_cmp(BT_EVENT_CON_CNF, event->errCode, event->p.remDev);
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_LINK_DISCONNECT:
|
|||
|
// if(user_bt_env.bt_disconnect_cb){
|
|||
|
// user_bt_env.bt_disconnect_cb(&event->p.bdAddr, event->errCode);
|
|||
|
// }
|
|||
|
bt_connect_act_cmp(BT_EVENT_DISCONNECT, event->errCode, event->p.remDev);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_ACCESSIBLE_CHANGE:
|
|||
|
//printf("access state = %d\r\n",event->p.aMode);
|
|||
|
// if(user_bt_env.bt_access_change_cb){
|
|||
|
// user_bt_env.bt_access_change_cb(event->p.aMode);
|
|||
|
// }
|
|||
|
bt_connect_act_cmp(BT_EVENT_ACC_CHG, event->p.aMode, NULL);
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_MODE_CHANGE:
|
|||
|
if(event->p.modeChange.curMode == 0){
|
|||
|
printf("Active Mode.\r\n");
|
|||
|
}else{
|
|||
|
printf("Sniff Mode.\r\n");
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case BTEVENT_ROLE_CHANGE:
|
|||
|
printf("role = %d\r\n",event->p.roleChange.newRole);
|
|||
|
break;
|
|||
|
case BTEVENT_HCI_COMMAND_SENT:
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void hf_callback(HfChannel *Chan, HfCallbackParms *Info)
|
|||
|
{
|
|||
|
// static bool scoPacketSent = true;
|
|||
|
|
|||
|
if ((Info->event != HF_EVENT_AUDIO_DATA)
|
|||
|
&& (Info->event != HF_EVENT_AUDIO_DATA_SENT)) {
|
|||
|
printf("hf_callback: event = %d.\r\n", Info->event);
|
|||
|
}
|
|||
|
|
|||
|
switch(Info->event){
|
|||
|
case HF_EVENT_GATEWAY_FEATURES:
|
|||
|
//printf("gw feature: %x\r\n",Info->p.features);
|
|||
|
if(Info->p.features & HFG_FEATURE_CODEC_NEGOTIATON){
|
|||
|
CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
}else{
|
|||
|
CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
}
|
|||
|
break;
|
|||
|
case HF_EVENT_SERVICE_CONNECT_REQ:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_CONN_REQ, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_SERVICE_CONNECTED:
|
|||
|
LOG_INFO(NULL,"hf connected\r\n");
|
|||
|
//A2DP_OpenStream(&Stream[0], &Info->p.remDev->bdAddr);
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_CONN, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_SERVICE_DISCONNECTED:
|
|||
|
LOG_INFO(NULL,"hf disconnected %d\r\n",Info->errCode);
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_DISCONN, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_CALL_IND:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_CALL, Chan, Info);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_CALLSETUP_IND:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_CALLSETUP, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_CALLHELD_IND:
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_RING_IND:
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_CALLER_ID_NOTIFY:
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_SPEAKER_VOLUME:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_SPK_VOL, Chan, Info);
|
|||
|
break;
|
|||
|
case HF_EVENT_CURRENT_CALL_STATE:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_CURRENT_CALL, Chan, Info);
|
|||
|
break;
|
|||
|
case HF_EVENT_AT_RESULT_DATA:
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_AT_RESULT, Chan, Info);
|
|||
|
break;
|
|||
|
case HF_EVENT_AUDIO_CONNECTED:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_SCO_CREATED;
|
|||
|
event.param.sco_codec.hf_channel = Chan;
|
|||
|
#if HF_CODEC_NEG == XA_ENABLED
|
|||
|
if (Chan->codecID == 2) {
|
|||
|
event.param.sco_codec.codec_type = APP_BTDM_CODEC_mSBC;
|
|||
|
CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
|
|||
|
}
|
|||
|
else if (Chan->codecID == 1) {
|
|||
|
event.param.sco_codec.codec_type = APP_BTDM_CODEC_PCM;
|
|||
|
CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
}
|
|||
|
else {
|
|||
|
assert(0);
|
|||
|
}
|
|||
|
#else
|
|||
|
event.param.sco_codec.codec_type = APP_BTDM_CODEC_PCM;
|
|||
|
#endif
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_AUDIO_CONN, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_AUDIO_DISCONNECTED:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_SCO_REMOVED;
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
bt_update_conn_status(BT_PROFILE_HF_AUDIO_DISCONN, Chan, Info);
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_AUDIO_DATA:
|
|||
|
if(Info->p.audioData->len > 60) {
|
|||
|
printf("sco receive length is %d.\r\n", Info->p.audioData->len);
|
|||
|
break;
|
|||
|
}
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_SCO_DATA;
|
|||
|
event.param.sco_data.valid = (Info->p.audioData->errFlags == 0);
|
|||
|
#if HF_CODEC_NEG == XA_ENABLED
|
|||
|
if (Chan->codecID == 2) {
|
|||
|
event.param.sco_data.codec_type = APP_BTDM_CODEC_mSBC;
|
|||
|
}
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
event.param.sco_data.codec_type = APP_BTDM_CODEC_PCM;
|
|||
|
}
|
|||
|
event.param.sco_data.buffer = Info->p.audioData->data;
|
|||
|
event.param.sco_data.length = Info->p.audioData->len;
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
// if (scoPacketSent) {
|
|||
|
// scoPacketSent = false;
|
|||
|
// memcpy(scoPacket->data, Info->p.audioData->data, Info->p.audioData->len);
|
|||
|
// scoPacket->dataLen = Info->p.audioData->len;
|
|||
|
// HF_SendAudioData(Chan, scoPacket);
|
|||
|
// }
|
|||
|
break;
|
|||
|
|
|||
|
case HF_EVENT_AUDIO_DATA_SENT:
|
|||
|
// scoPacketSent = true;
|
|||
|
btdm_free(Info->p.audioPacket->data);
|
|||
|
btdm_free(Info->p.audioPacket);
|
|||
|
break;
|
|||
|
|
|||
|
#if HF_CODEC_NEG == XA_ENABLED
|
|||
|
case HF_EVENT_CODEC_NEGOTIATION:
|
|||
|
{
|
|||
|
HfCommand *cmd;
|
|||
|
|
|||
|
if(Info->p.codecID == 1){
|
|||
|
//cvsd
|
|||
|
CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
}else if(Info->p.codecID == 2){
|
|||
|
//msbc
|
|||
|
CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
}
|
|||
|
BtStatus ret = BT_STATUS_NO_RESOURCES;
|
|||
|
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
|
|||
|
if(cmd != NULL){
|
|||
|
hf_codec[0] = Info->p.codecID + '0';
|
|||
|
hf_codec[1] = 0;
|
|||
|
ret = HF_CodecConnectionSetup((HfChannel *)Chan, hf_codec, cmd);
|
|||
|
}
|
|||
|
if(ret != BT_STATUS_PENDING){
|
|||
|
btdm_free((void *)cmd);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
#endif
|
|||
|
|
|||
|
case HF_EVENT_COMMAND_COMPLETE:
|
|||
|
btdm_free((uint8_t *)Info->p.command);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void hfg_callback(HfgChannel *Chan, HfgCallbackParms *Info)
|
|||
|
{
|
|||
|
BtStatus status;
|
|||
|
HfgResponse *rsp;
|
|||
|
|
|||
|
if ((Info->hfgEvent != HFG_EVENT_AUDIO_DATA)
|
|||
|
&& (Info->hfgEvent != HFG_EVENT_AUDIO_DATA_SENT)) {
|
|||
|
LOG_INFO(NULL, "hfg_callback :%d\r\n",Info->hfgEvent);
|
|||
|
}
|
|||
|
switch (Info->hfgEvent) {
|
|||
|
case HFG_EVENT_HANDSFREE_FEATURES:
|
|||
|
printf("remote hf feature: %x\r\n",Info->p.features);
|
|||
|
// if(Info->p.features & HF_FEATURE_CODEC_NEGOTIATION){
|
|||
|
// CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
// }else{
|
|||
|
// CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
// }
|
|||
|
break;
|
|||
|
case HFG_EVENT_SERVICE_CONNECT_REQ:
|
|||
|
//App_Report("Incoming Channel");
|
|||
|
break;
|
|||
|
|
|||
|
case HFG_EVENT_SERVICE_CONNECTED:
|
|||
|
A2DP_OpenStream(&Stream[2], &Info->p.remDev->bdAddr);
|
|||
|
break;
|
|||
|
case HFG_EVENT_AT_COMMAND_DATA:
|
|||
|
for(uint8_t i = 0; i < Info->p.data->dataLen; i++)
|
|||
|
printf("%c",Info->p.data->rawData[i]);
|
|||
|
printf("\r\n");
|
|||
|
|
|||
|
rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
|
|||
|
status = HFG_SendOK(Chan,rsp);
|
|||
|
if(status != BT_STATUS_PENDING){
|
|||
|
btdm_free((void *)rsp);
|
|||
|
}
|
|||
|
// if(memcmp(Info->p.data->rawData,"AT+IPHONEACCEV=1,1,9",sizeof("AT+IPHONEACCEV=1,1,9")) == 0)
|
|||
|
// {
|
|||
|
// status = BT_STATUS_NO_RESOURCES;
|
|||
|
// rsp = (HfgResponse *)btdm_malloc(sizeof(HfgResponse));
|
|||
|
// if(rsp != NULL){
|
|||
|
// status = HFG_CreateCodecConnection(&hfg_channel[0], 1, rsp);
|
|||
|
// }
|
|||
|
// if(status != BT_STATUS_PENDING){
|
|||
|
// btdm_free((void *)rsp);
|
|||
|
// }
|
|||
|
// printf("status = %d\r\n",status);
|
|||
|
// }
|
|||
|
break;
|
|||
|
case HFG_EVENT_SUPPORTED_CODEC:
|
|||
|
printf("hf supported codec %x\r\n",Info->p.features);
|
|||
|
if(Info->p.features == 2){
|
|||
|
CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
}else{
|
|||
|
CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
}
|
|||
|
break;
|
|||
|
case HFG_EVENT_AUDIO_DATA:
|
|||
|
{
|
|||
|
#if 0
|
|||
|
if(Info->p.audioData->len > 60) {
|
|||
|
printf("sco receive length is %d.\r\n", Info->p.audioData->len);
|
|||
|
break;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if 0
|
|||
|
for(uint8_t i = 0; i < Info->p.audioData->len; i++)
|
|||
|
{
|
|||
|
fputc(Info->p.audioData->data[i],NULL);
|
|||
|
}
|
|||
|
#else
|
|||
|
// if(Info->p.audioData->len == 120){
|
|||
|
// fputc('R',NULL);
|
|||
|
// }else if(Info->p.audioData->len == 60){
|
|||
|
// fputc('r',NULL);
|
|||
|
// }
|
|||
|
|
|||
|
BtPacket *sco_packet;
|
|||
|
sco_packet = btdm_malloc(sizeof(BtPacket));
|
|||
|
memset((void *)sco_packet, 0, sizeof(BtPacket));
|
|||
|
sco_packet->data = btdm_malloc(120);
|
|||
|
memcpy(sco_packet->data, Info->p.audioData->data, Info->p.audioData->len);
|
|||
|
sco_packet->dataLen = Info->p.audioData->len;
|
|||
|
status = HF_SendAudioData(Chan, sco_packet);
|
|||
|
if(status != BT_STATUS_PENDING){
|
|||
|
printf("no pending:%d\r\n",status);
|
|||
|
btdm_free((void *)sco_packet->data);
|
|||
|
btdm_free((void *)sco_packet);
|
|||
|
}
|
|||
|
|
|||
|
//if(Info->p.audioData->errFlags != 0){
|
|||
|
// fputc('a'+Info->p.audioData->errFlags,NULL);
|
|||
|
//}
|
|||
|
#endif
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case HFG_EVENT_AUDIO_DATA_SENT:
|
|||
|
//fputc('s',NULL);
|
|||
|
btdm_free((void *)Info->p.audioPacket->data);
|
|||
|
btdm_free((void *)Info->p.audioPacket);
|
|||
|
break;
|
|||
|
|
|||
|
case HFG_EVENT_CODEC_CONNECTION_RSP:
|
|||
|
printf("hfg codec connection rsp: %d\r\n",Info->p.features);
|
|||
|
if(Info->p.features == 2){
|
|||
|
CMGR_SetAudioVoiceSettings(0x63);
|
|||
|
}else{
|
|||
|
CMGR_SetAudioVoiceSettings(0x60);
|
|||
|
}
|
|||
|
HFG_CreateAudioLink(&hfg_channel[0],HFG_AR_LOCAL_USER_ACTION);
|
|||
|
break;
|
|||
|
case HFG_EVENT_RESPONSE_COMPLETE:
|
|||
|
|
|||
|
btdm_free((void *)Info->p.response);
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void a2dp_callback(A2dpStream *cbStream, const A2dpCallbackParms *Parms)
|
|||
|
{
|
|||
|
AvdtpCodec *codec;
|
|||
|
uint8_t *elements;
|
|||
|
uint8_t *reqElements;
|
|||
|
uint8_t error = A2DP_ERR_NO_ERROR;
|
|||
|
|
|||
|
if(Parms->event == 18) {
|
|||
|
}
|
|||
|
else {
|
|||
|
LOG_INFO(NULL, "a2dp callback event=%d,%x,%x\r\n", Parms->event,Parms->error,Parms->discReason);
|
|||
|
}
|
|||
|
switch(Parms->event) {
|
|||
|
case A2DP_EVENT_STREAM_OPEN_IND:
|
|||
|
bt_update_conn_status(BT_PROFILE_A2DP_OPEN_IND, cbStream, Parms);
|
|||
|
if (AVDTP_CODEC_TYPE_SBC == Parms->p.configReq->codec.codecType) {
|
|||
|
codec = A2DP_GetRegisteredCodec(cbStream);
|
|||
|
elements = codec->elements;
|
|||
|
reqElements = Parms->p.configReq->codec.elements;
|
|||
|
|
|||
|
/* Check requested elements with registered elements */
|
|||
|
if (!(reqElements[0] & (elements[0] & 0xF0))) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_SAMP_FREQ;
|
|||
|
} else if (!(reqElements[0] & (elements[0] & 0x0F))) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_CHANNEL_MODE;
|
|||
|
} else if (!(reqElements[1] & (elements[1] & 0x0C))) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_SUBBANDS;
|
|||
|
} else if (!(reqElements[1] & (elements[1] & 0x03))) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_ALLOC_METHOD;
|
|||
|
} else if (reqElements[2] < elements[2]) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_MIN_BITPOOL_VALUE;
|
|||
|
} else if (reqElements[3] > elements[3]) {
|
|||
|
error = A2DP_ERR_NOT_SUPPORTED_MAX_BITPOOL_VALUE;
|
|||
|
}
|
|||
|
|
|||
|
// codec->freq = (reqElements[0] & 0xF0) >> 5;
|
|||
|
A2DP_OpenStreamRspWithSinkDelay(cbStream, error,
|
|||
|
AVDTP_SRV_CAT_MEDIA_CODEC,
|
|||
|
0);
|
|||
|
}
|
|||
|
else if(AVDTP_CODEC_TYPE_MPEG2_4_AAC== Parms->p.configReq->codec.codecType) {
|
|||
|
codec = A2DP_GetRegisteredCodec(cbStream);
|
|||
|
elements = codec->elements;
|
|||
|
reqElements = Parms->p.configReq->codec.elements;
|
|||
|
// if(reqElements[1] & 0x01) {
|
|||
|
// codec->freq = 1;
|
|||
|
// }
|
|||
|
// else if(reqElements[2] & 0x80) {
|
|||
|
// codec->freq = 0;
|
|||
|
// }
|
|||
|
|
|||
|
A2DP_OpenStreamRspWithSinkDelay(cbStream, error,
|
|||
|
AVDTP_SRV_CAT_MEDIA_CODEC,
|
|||
|
0);
|
|||
|
}
|
|||
|
else {
|
|||
|
/* Refuse to accept incoming con to MP3 stream for now */
|
|||
|
A2DP_OpenStreamRspWithSinkDelay(cbStream,
|
|||
|
AVRCP_ERR_UNKNOWN_ERROR,
|
|||
|
AVDTP_SRV_CAT_MEDIA_CODEC,
|
|||
|
0);
|
|||
|
}
|
|||
|
break;
|
|||
|
case A2DP_EVENT_CODEC_INFO:
|
|||
|
/* Found a matching codec */
|
|||
|
codec = A2DP_GetRegisteredCodec(cbStream);
|
|||
|
elements = codec->elements;
|
|||
|
|
|||
|
/* Save the remote codec information. Selection
|
|||
|
* of capabilities will be made from the UI.
|
|||
|
*/
|
|||
|
cfgCodec.codecType = Parms->p.codec->codecType;
|
|||
|
cfgCodec.elemLen = Parms->p.codec->elemLen;
|
|||
|
|
|||
|
cfgCodec.elements = cfgElements;
|
|||
|
if (cfgCodec.codecType == AVDTP_CODEC_TYPE_SBC) {
|
|||
|
|
|||
|
/* Only the matching codec information
|
|||
|
* elements can be selected.
|
|||
|
*/
|
|||
|
|
|||
|
cfgCodec.elements[0]
|
|||
|
= (uint8_t)(Parms->p.codec->elements[0] & elements[0]);
|
|||
|
cfgCodec.elements[1]
|
|||
|
= (uint8_t)(Parms->p.codec->elements[1] & elements[1]);
|
|||
|
cfgCodec.elements[2]
|
|||
|
= (uint8_t)(max(Parms->p.codec->elements[2], elements[2]));
|
|||
|
cfgCodec.elements[3]
|
|||
|
= (uint8_t)(min(Parms->p.codec->elements[3], elements[3]));
|
|||
|
}
|
|||
|
break;
|
|||
|
case A2DP_EVENT_GET_CONFIG_IND:
|
|||
|
LOG_INFO(NULL, "codec type=%d,%x,%x,%x,%x\r\n",cfgCodec.codecType,cfgCodec.elements[0],cfgCodec.elements[1],cfgCodec.elements[2],cfgCodec.elements[3]);
|
|||
|
/* Make sure something valid is configured for each field */
|
|||
|
if (cfgCodec.codecType == AVDTP_CODEC_TYPE_SBC) {
|
|||
|
|
|||
|
if ( (cfgCodec.elements[0] & 0xF0)
|
|||
|
&& (cfgCodec.elements[0] & 0x0F)
|
|||
|
&& (cfgCodec.elements[1] & 0xF0)
|
|||
|
&& (cfgCodec.elements[1] & 0x0C)
|
|||
|
&& (cfgCodec.elements[1] & 0x03)) {
|
|||
|
|
|||
|
/* Pick the sampling rate supported */
|
|||
|
if (cfgCodec.elements[0] & 0x80) {
|
|||
|
cfgCodec.elements[0] &= 0x8F;
|
|||
|
} else if (cfgCodec.elements[0] & 0x40) {
|
|||
|
cfgCodec.elements[0] &= 0x4F;
|
|||
|
} else if (cfgCodec.elements[0] & 0x20) {
|
|||
|
cfgCodec.elements[0] &= 0x2F;
|
|||
|
} else if (cfgCodec.elements[0] & 0x10) {
|
|||
|
cfgCodec.elements[0] &= 0x1F;
|
|||
|
}
|
|||
|
|
|||
|
/* Pick the channel mode */
|
|||
|
if (cfgCodec.elements[0] & 0x08) {
|
|||
|
cfgCodec.elements[0] &= 0xF8;
|
|||
|
} else if (cfgCodec.elements[0] & 0x04) {
|
|||
|
cfgCodec.elements[0] &= 0xF4;
|
|||
|
} else if (cfgCodec.elements[0] & 0x02) {
|
|||
|
cfgCodec.elements[0] &= 0xF2;
|
|||
|
} else if (cfgCodec.elements[0] & 0x01) {
|
|||
|
cfgCodec.elements[0] &= 0xF1;
|
|||
|
}
|
|||
|
|
|||
|
/* Pick the block length */
|
|||
|
if (cfgCodec.elements[1] & 0x80) {
|
|||
|
cfgCodec.elements[1] &= 0x8F;
|
|||
|
} else if (cfgCodec.elements[1] & 0x40) {
|
|||
|
cfgCodec.elements[1] &= 0x4F;
|
|||
|
} else if (cfgCodec.elements[1] & 0x20) {
|
|||
|
cfgCodec.elements[1] &= 0x2F;
|
|||
|
} else if (cfgCodec.elements[1] & 0x10) {
|
|||
|
cfgCodec.elements[1] &= 0x1F;
|
|||
|
}
|
|||
|
|
|||
|
/* Pick the number of subbands */
|
|||
|
if (cfgCodec.elements[1] & 0x08) {
|
|||
|
cfgCodec.elements[1] &= 0xFB;
|
|||
|
} else if (cfgCodec.elements[1] & 0x04) {
|
|||
|
cfgCodec.elements[1] &= 0xF7;
|
|||
|
}
|
|||
|
|
|||
|
/* Pick the allocation method */
|
|||
|
if (cfgCodec.elements[1] & 0x02) {
|
|||
|
cfgCodec.elements[1] &= 0xFE;
|
|||
|
} else if (cfgCodec.elements[1] & 0x01) {
|
|||
|
cfgCodec.elements[1] &= 0xFD;
|
|||
|
}
|
|||
|
|
|||
|
/* Select Min/Max Bitpools */
|
|||
|
cfgCodec.elements[2] = cfgCodec.elements[2];
|
|||
|
cfgCodec.elements[3] = cfgCodec.elements[3];
|
|||
|
A2DP_SetStreamConfigWithSinkDelay(cbStream, &cfgCodec, 0, 0);
|
|||
|
}
|
|||
|
else{
|
|||
|
A2DP_CloseStream(cbStream);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_OPEN:
|
|||
|
LOG_INFO(NULL,"a2dp connected\r\n");
|
|||
|
bt_update_conn_status(BT_PROFILE_A2DP_CONN, cbStream, Parms);
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_START_IND:
|
|||
|
A2DP_StartStreamRsp(cbStream, A2DP_ERR_NO_ERROR);
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_STARTED:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
|
|||
|
event.event = APP_BTDM_EVT_A2DP_STREAM_STARTED;
|
|||
|
codec = A2DP_GetRegisteredCodec(cbStream);
|
|||
|
if (codec->codecType == AVDTP_CODEC_TYPE_MPEG2_4_AAC) {
|
|||
|
event.param.a2dp_codec.codec_type = APP_BTDM_CODEC_AAC;
|
|||
|
if (codec->elements[1] & A2DP_AAC_CODEC_FREQ_44100) {
|
|||
|
event.param.a2dp_codec.sample_rate = 44100;
|
|||
|
}
|
|||
|
else if (codec->elements[2] & A2DP_AAC_CODEC_FREQ_48000) {
|
|||
|
event.param.a2dp_codec.sample_rate = 48000;
|
|||
|
}
|
|||
|
else {
|
|||
|
assert(0);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (codec->codecType == AVDTP_CODEC_TYPE_SBC) {
|
|||
|
event.param.a2dp_codec.codec_type = APP_BTDM_CODEC_SBC;
|
|||
|
if (codec->elements[0] & A2DP_SBC_CODEC_FREQ_44100) {
|
|||
|
event.param.a2dp_codec.sample_rate = 44100;
|
|||
|
}
|
|||
|
else if (codec->elements[0] & A2DP_SBC_CODEC_FREQ_48000) {
|
|||
|
event.param.a2dp_codec.sample_rate = 48000;
|
|||
|
}
|
|||
|
else {
|
|||
|
assert(0);
|
|||
|
}
|
|||
|
}
|
|||
|
else {
|
|||
|
event.param.a2dp_codec.codec_type = APP_BTDM_CODEC_UNKNOWN;
|
|||
|
}
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
|
|||
|
bt_update_conn_status(BT_PROFILE_A2DP_PLAYING, cbStream, Parms);
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_SUSPENDED:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_A2DP_STREAM_STOPPED;
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
bt_update_conn_status(BT_PROFILE_A2DP_SUSPEND, cbStream, Parms);
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_CLOSED:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_A2DP_STREAM_STOPPED;
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
bt_update_conn_status(BT_PROFILE_A2DP_DISCONN, cbStream, Parms);
|
|||
|
break;
|
|||
|
case A2DP_EVENT_STREAM_DATA_IND:
|
|||
|
if (btdm_cb) {
|
|||
|
struct app_btdm_event_t event;
|
|||
|
event.event = APP_BTDM_EVT_A2DP_STREAM_DATA;
|
|||
|
event.param.a2dp_data.buffer = Parms->p.data + 13;
|
|||
|
event.param.a2dp_data.length = Parms->len - 13;
|
|||
|
btdm_cb(&event);
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void avrcp_callback(AvrcpChannel *chnl, const AvrcpCallbackParms *Parms)
|
|||
|
{
|
|||
|
AvrcpAdvancedPdu *avrcp_cmd;
|
|||
|
BtStatus status;
|
|||
|
|
|||
|
LOG_INFO(NULL, "avrcp callback event=%d, status=%d, chnl=%x\r\n",Parms->event, Parms->status, chnl);
|
|||
|
|
|||
|
|
|||
|
switch (Parms->event) {
|
|||
|
case AVRCP_EVENT_CONNECT_IND:
|
|||
|
AVRCP_ConnectRsp(chnl,TRUE);
|
|||
|
break;
|
|||
|
case AVRCP_EVENT_CONNECT:
|
|||
|
{
|
|||
|
avrcp_cmd = btdm_malloc(sizeof(AvrcpAdvancedPdu));
|
|||
|
avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
|
|||
|
status = AVRCP_CtGetCapabilities((AvrcpChannel *)chnl, avrcp_cmd, AVRCP_CAPABILITY_EVENTS_SUPPORTED);
|
|||
|
if (status != BT_STATUS_PENDING) {
|
|||
|
btdm_free(avrcp_cmd->parms);
|
|||
|
btdm_free(avrcp_cmd);
|
|||
|
}
|
|||
|
AVRCP_TgSetEventMask(chnl,AVRCP_ENABLE_VOLUME_CHANGED);
|
|||
|
}
|
|||
|
LOG_INFO(NULL, "avrcp connected\r\n");
|
|||
|
bt_update_conn_status(BT_PROFILE_AVRCP_CONN, chnl, Parms);
|
|||
|
break;
|
|||
|
case AVRCP_EVENT_DISCONNECT:
|
|||
|
LOG_INFO(NULL, "avrcp disconnected\r\n");
|
|||
|
bt_update_conn_status(BT_PROFILE_AVRCP_DISCONN, chnl, Parms);
|
|||
|
break;
|
|||
|
|
|||
|
case AVRCP_EVENT_ADV_INFO:
|
|||
|
if(Parms->advOp == AVRCP_OP_SET_ABSOLUTE_VOLUME) {
|
|||
|
//LOG_INFO("SET_ABSOLUTE_VOLUME is %d.\r\n", event->param.adv.info.volume);
|
|||
|
printf("+VOL:%02x\r\n",Parms->p.adv.info.volume);
|
|||
|
}
|
|||
|
break;
|
|||
|
case AVRCP_EVENT_ADV_RESPONSE:
|
|||
|
///本地查询TG支持事件,返回消<E59B9E><E6B688>?
|
|||
|
/**/
|
|||
|
if((Parms->advOp == AVRCP_OP_GET_CAPABILITIES)
|
|||
|
&&(Parms->p.adv.rsp.capability.type == AVRCP_CAPABILITY_EVENTS_SUPPORTED)){
|
|||
|
//printf("cap eventmaskk = %x\r\n",event->param.adv.rsp.capability.info.eventMask);
|
|||
|
|
|||
|
if(Parms->p.adv.rsp.capability.info.eventMask & AVRCP_ENABLE_PLAY_STATUS_CHANGED){
|
|||
|
///注册播放状态改变事件,当TG播放状态改变时,产生AVRCP_EVENT_ADV_NOTIFY事件
|
|||
|
avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
|
|||
|
avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
|
|||
|
status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_MEDIA_STATUS_CHANGED, 0);
|
|||
|
if (status != BT_STATUS_PENDING) {
|
|||
|
btdm_free(avrcp_cmd->parms);
|
|||
|
btdm_free(avrcp_cmd);
|
|||
|
}
|
|||
|
}
|
|||
|
if(Parms->p.adv.rsp.capability.info.eventMask & AVRCP_ENABLE_TRACK_CHANGED){
|
|||
|
///注册歌曲改变事件,当TG播放状态改变时,产生AVRCP_EVENT_ADV_NOTIFY事件
|
|||
|
avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
|
|||
|
avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
|
|||
|
status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_TRACK_CHANGED, 0);
|
|||
|
if (status != BT_STATUS_PENDING) {
|
|||
|
btdm_free(avrcp_cmd->parms);
|
|||
|
btdm_free(avrcp_cmd);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
///本地注册事件,返回的当前状<E5898D><E78AB6>?
|
|||
|
if(Parms->advOp == AVRCP_OP_REGISTER_NOTIFY) {
|
|||
|
///播放状态改变通知的当前状<E5898D><E78AB6>?
|
|||
|
if(Parms->p.adv.notify.event == AVRCP_EID_MEDIA_STATUS_CHANGED) {
|
|||
|
|
|||
|
}
|
|||
|
else if(Parms->p.adv.notify.event == AVRCP_EID_TRACK_CHANGED){
|
|||
|
//user_bt_get_media_info();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(Parms->advOp == AVRCP_OP_GET_MEDIA_INFO){
|
|||
|
uint16_t len0 = Parms->p.adv.rsp.element.txt[0].length;
|
|||
|
uint16_t len1 = Parms->p.adv.rsp.element.txt[1].length;
|
|||
|
printf("media info: %d,%s,%d\r\n",len0,Parms->p.adv.rsp.element.txt[0].string,len1);
|
|||
|
}
|
|||
|
|
|||
|
if(Parms->advOp == AVRCP_OP_GET_PLAY_STATUS){
|
|||
|
printf("media length:%d,pos:%d,status:%d\r\n",Parms->p.adv.rsp.playStatus.length,Parms->p.adv.rsp.playStatus.position,Parms->p.adv.rsp.playStatus.mediaStatus);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case AVRCP_EVENT_PANEL_CNF:
|
|||
|
if(Parms->p.panelCnf.press == TRUE){
|
|||
|
switch(Parms->p.panelCnf.operation) {
|
|||
|
case AVRCP_POP_PAUSE:
|
|||
|
AVRCP_SetPanelKey(chnl, AVRCP_POP_PAUSE, FALSE);
|
|||
|
break;
|
|||
|
case AVRCP_POP_PLAY:
|
|||
|
AVRCP_SetPanelKey(chnl, AVRCP_POP_PLAY, FALSE);
|
|||
|
break;
|
|||
|
case AVRCP_POP_FORWARD:
|
|||
|
AVRCP_SetPanelKey(chnl, AVRCP_POP_FORWARD, FALSE);
|
|||
|
break;
|
|||
|
case AVRCP_POP_BACKWARD:
|
|||
|
AVRCP_SetPanelKey(chnl, AVRCP_POP_BACKWARD, FALSE);
|
|||
|
break;
|
|||
|
case AVRCP_POP_STOP:
|
|||
|
AVRCP_SetPanelKey(chnl, AVRCP_POP_STOP, FALSE);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case AVRCP_EVENT_ADV_NOTIFY:
|
|||
|
{
|
|||
|
const AvrcpAdvNotifyParms *avrcp_ntf = &Parms->p.adv.notify;
|
|||
|
if(Parms->errorCode == AVRCP_ERR_NO_ERROR) {
|
|||
|
switch(avrcp_ntf->event) {
|
|||
|
case AVRCP_EID_TRACK_CHANGED:
|
|||
|
{
|
|||
|
avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
|
|||
|
avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
|
|||
|
status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_TRACK_CHANGED, 0);
|
|||
|
if (status != BT_STATUS_PENDING) {
|
|||
|
btdm_free(avrcp_cmd->parms);
|
|||
|
btdm_free(avrcp_cmd);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case AVRCP_EID_MEDIA_STATUS_CHANGED:
|
|||
|
{
|
|||
|
///重新注册播放状态改变通知消息
|
|||
|
uint8_t media_status = Parms->p.adv.notify.p.mediaStatus;
|
|||
|
bt_update_conn_status(BT_PROFILE_AVRCP_MEDIA_STATUS, chnl, &media_status);
|
|||
|
|
|||
|
avrcp_cmd = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
|
|||
|
avrcp_cmd->parms = (uint8_t *)btdm_malloc(64);
|
|||
|
status = AVRCP_CtRegisterNotification(chnl, avrcp_cmd, AVRCP_EID_MEDIA_STATUS_CHANGED, 0);
|
|||
|
if (status != BT_STATUS_PENDING) {
|
|||
|
btdm_free(avrcp_cmd->parms);
|
|||
|
btdm_free(avrcp_cmd);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case AVRCP_EVENT_ADV_TX_DONE:
|
|||
|
btdm_free(Parms->p.adv.txPdu->parms);
|
|||
|
btdm_free(Parms->p.adv.txPdu);
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void pbap_callback(PbapClientCallbackParms *Parms)
|
|||
|
{
|
|||
|
printf("pbap callback event = %d\r\n",Parms->event);
|
|||
|
switch (Parms->event) {
|
|||
|
case PBAP_EVENT_PARAMS_RX:
|
|||
|
{
|
|||
|
if ((Parms->oper == PBAPOP_PULL_PHONEBOOK) ||
|
|||
|
(Parms->oper == PBAPOP_PULL_VCARD_LISTING)) {
|
|||
|
printf("miss calls: %d,pb size=%d\r\n",Parms->u.paramsRx.newMissedCalls,Parms->u.paramsRx.phonebookSize);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
case PBAP_EVENT_DATA_IND:
|
|||
|
bt_update_conn_status(BT_PROFILE_PBAP_DATA_IND, NULL, Parms);
|
|||
|
break;
|
|||
|
case PBAP_EVENT_TP_CONNECTED:
|
|||
|
bt_update_conn_status(BT_PROFILE_PBAP_CONN, NULL, Parms);
|
|||
|
break;
|
|||
|
case PBAP_EVENT_TP_DISCONNECTED:
|
|||
|
bt_update_conn_status(BT_PROFILE_PBAP_DISCONN, NULL, Parms);
|
|||
|
break;
|
|||
|
case PBAP_EVENT_CONTINUE:
|
|||
|
/* Always call continue to keep the commands flowing */
|
|||
|
PBAP_ClientContinue(Parms->client);
|
|||
|
break;
|
|||
|
case PBAP_EVENT_ABORTED:
|
|||
|
/* The requested operation was aborted. */
|
|||
|
break;
|
|||
|
|
|||
|
case PBAP_EVENT_COMPLETE:
|
|||
|
bt_update_conn_status(BT_PROFILE_PBAP_COMP, NULL, Parms);
|
|||
|
break;
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void spp_callback(SppDev *locDev, SppCallbackParms *Info)
|
|||
|
{
|
|||
|
printf("spp callback, event = %d\r\n",Info->event);
|
|||
|
switch(Info->event) {
|
|||
|
case SPP_EVENT_REMDEV_CONNECTED:
|
|||
|
|
|||
|
printf("spp connected\r\n");
|
|||
|
break;
|
|||
|
case SPP_EVENT_REMDEV_DISCONNECTED:
|
|||
|
printf("spp disconnected\r\n");
|
|||
|
break;
|
|||
|
case SPP_EVENT_DATA_IND:
|
|||
|
printf("spp recv: ");
|
|||
|
for(uint8_t i = 0; i < Info->p.pkt->dataLen; i++)
|
|||
|
{
|
|||
|
printf("%c",Info->p.pkt->data[i]);
|
|||
|
}
|
|||
|
printf("\r\n");
|
|||
|
break;
|
|||
|
case SPP_EVENT_SEND_COMPLETE:
|
|||
|
btdm_free(Info->p.pkt->data);
|
|||
|
btdm_free((uint8_t *)Info->p.pkt);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//void a2dp_device_callback(void *Device, const void *Parms)
|
|||
|
//{
|
|||
|
// switch(*(volatile uint8_t *)Parms){
|
|||
|
// case 1/*AVDEV_EVENT_AVDTP_CONNECT_IND*/:
|
|||
|
// AVDEV_SignalConnectRsp(Device, TRUE, a2dp_device_callback);
|
|||
|
// break;
|
|||
|
// default:
|
|||
|
// break;
|
|||
|
// }
|
|||
|
//}
|
|||
|
|
|||
|
void app_btdm_struct_size_dump(void)
|
|||
|
{
|
|||
|
printf("BTDM STRUCT SIZE: defined in stack.\r\n");
|
|||
|
extern void btdm_struct_size_dump(void);
|
|||
|
btdm_struct_size_dump();
|
|||
|
|
|||
|
printf("BTDM STRUCT SIZE: defined in api.\r\n");
|
|||
|
printf("BtEvent is %d\r\n", sizeof(BtEvent));
|
|||
|
printf("BtRemoteDevice is %d\r\n", sizeof(BtRemoteDevice));
|
|||
|
|
|||
|
printf("RfDeferEvent is %d\r\n", sizeof(RfDeferEvent));
|
|||
|
printf("RfChannel is %d\r\n", sizeof(RfChannel));
|
|||
|
|
|||
|
printf("HfChannel is %d\r\n", sizeof(HfChannel));
|
|||
|
printf("HfCommand is %d\r\n", sizeof(HfCommand));
|
|||
|
printf("HfCallbackParms is %d\r\n", sizeof(HfCallbackParms));
|
|||
|
printf("HfgChannel is %d\r\n", sizeof(HfgChannel));
|
|||
|
printf("HfgCallbackParms is %d\r\n", sizeof(HfgCallbackParms));
|
|||
|
|
|||
|
printf("A2dpStream is %d\r\n", sizeof(A2dpStream));
|
|||
|
printf("A2dpCallbackParms is %d\r\n", sizeof(A2dpCallbackParms));
|
|||
|
|
|||
|
printf("AvdtpStream is %d\r\n", sizeof(AvdtpStream));
|
|||
|
printf("AvdtpConn is %d\r\n", sizeof(AvdtpConn));
|
|||
|
printf("AvdtpCallbackParms is %d\r\n", sizeof(AvdtpCallbackParms));
|
|||
|
|
|||
|
printf("AvctpCallbackParms is %d\r\n", sizeof(AvctpCallbackParms));
|
|||
|
printf("AvctpChannel is %d\r\n", sizeof(AvctpChannel));
|
|||
|
printf("AvctpCmdFrame is %d\r\n", sizeof(AvctpCmdFrame));
|
|||
|
printf("AvctpRspFrame is %d\r\n", sizeof(AvctpRspFrame));
|
|||
|
|
|||
|
printf("AvrcpChannel is %d\r\n", sizeof(AvrcpChannel));
|
|||
|
printf("AvrcpCallbackParms is %d\r\n", sizeof(AvrcpCallbackParms));
|
|||
|
printf("AvrcpAdvancedPdu is %d\r\n", sizeof(AvrcpAdvancedPdu));
|
|||
|
printf("AvrcpPlayerSetting is %d\r\n", sizeof(AvrcpPlayerSetting));
|
|||
|
printf("AvrcpPlayerStrings is %d\r\n", sizeof(AvrcpPlayerStrings));
|
|||
|
printf("AvrcpEqString is %d\r\n", sizeof(AvrcpEqString));
|
|||
|
printf("AvrcpRepeatString is %d\r\n", sizeof(AvrcpRepeatString));
|
|||
|
printf("AvrcpShuffleString is %d\r\n", sizeof(AvrcpShuffleString));
|
|||
|
printf("AvrcpScanString is %d\r\n", sizeof(AvrcpScanString));
|
|||
|
printf("AvrcpExtString is %d\r\n", sizeof(AvrcpExtString));
|
|||
|
printf("AvrcpCharSets is %d\r\n", sizeof(AvrcpCharSets));
|
|||
|
printf("AvrcpMediaInfo is %d\r\n", sizeof(AvrcpMediaInfo));
|
|||
|
printf("AvrcpMediaPlayStatus is %d\r\n", sizeof(AvrcpMediaPlayStatus));
|
|||
|
printf("AvrcpTrackStruct is %d\r\n", sizeof(AvrcpTrackStruct));
|
|||
|
}
|
|||
|
|
|||
|
void app_bt_send_sco_data(void *channel, uint8_t seq, uint8_t *data, uint16_t length)
|
|||
|
{
|
|||
|
BtPacket *sco_packet;
|
|||
|
HfChannel *Chan = channel;
|
|||
|
uint16_t data_length;
|
|||
|
uint8_t sub_seq = 0;
|
|||
|
|
|||
|
sco_packet = btdm_malloc(sizeof(BtPacket));
|
|||
|
if (sco_packet) {
|
|||
|
memset((void *)sco_packet, 0, sizeof(BtPacket));
|
|||
|
|
|||
|
if (length == 57) {
|
|||
|
data_length = 60;
|
|||
|
sco_packet->data = btdm_malloc(data_length);
|
|||
|
sco_packet->data[0] = 0x01;
|
|||
|
if (seq & 0x01) {
|
|||
|
sub_seq = 0x30;
|
|||
|
}
|
|||
|
if (seq & 0x02) {
|
|||
|
sub_seq |= 0xc0;
|
|||
|
}
|
|||
|
sco_packet->data[1] = 0x08 | sub_seq;
|
|||
|
memcpy(&sco_packet->data[2], data, length);
|
|||
|
sco_packet->data[59] = 0x00;
|
|||
|
sco_packet->dataLen = data_length;
|
|||
|
}
|
|||
|
else {
|
|||
|
sco_packet->data = btdm_malloc(length);
|
|||
|
memcpy(sco_packet->data, data, length);
|
|||
|
sco_packet->dataLen = length;
|
|||
|
}
|
|||
|
|
|||
|
HF_SendAudioData(Chan, sco_packet);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void app_bt_init(app_btdm_callback_t cb)
|
|||
|
{
|
|||
|
// app_btdm_struct_size_dump();
|
|||
|
#define EIR_DATA_SIZE 50
|
|||
|
uint8_t eir_data[EIR_DATA_SIZE];
|
|||
|
uint8_t index = 0;
|
|||
|
uint8_t rand_num[4];
|
|||
|
uint8_t hex_2_char[] = {'0', '1', '2','3', '4', '5','6', '7', '8','9', 'a', 'b','c', 'd', 'e', 'f'};
|
|||
|
|
|||
|
btdm_cb = cb;
|
|||
|
#if BTDM_STACK_ENABLE_HF
|
|||
|
hf_channel = btdm_malloc(sizeof(HfChannel) * NUM_BT_DEVICES);
|
|||
|
#endif
|
|||
|
#if BTDM_STACK_ENABLE_AG
|
|||
|
hfg_channel = btdm_malloc(sizeof(HfgChannel) * NUM_BT_DEVICES);
|
|||
|
#endif
|
|||
|
Stream = btdm_malloc(sizeof(A2dpStream) * NUM_STREAMS);
|
|||
|
#if BTDM_STACK_ENABLE_AVRCP
|
|||
|
rcpCtChannel = btdm_malloc(sizeof(AvrcpChannel) * NUM_BT_DEVICES);
|
|||
|
rcpTgChannel = btdm_malloc(sizeof(AvrcpChannel) * NUM_BT_DEVICES);
|
|||
|
#endif
|
|||
|
#if BTDM_STACK_ENABLE_PBAP
|
|||
|
pbap_client = btdm_malloc(sizeof(PbapClientSession) * NUM_BT_DEVICES);
|
|||
|
#endif
|
|||
|
#if BTDM_STACK_ENABLE_SPP
|
|||
|
spp_dev = btdm_malloc(sizeof(SppDev) * NUM_BT_DEVICES);
|
|||
|
#endif
|
|||
|
|
|||
|
// scoPacket = btdm_malloc(sizeof(BtPacket));
|
|||
|
// memset((void *)scoPacket, 0, sizeof(BtPacket));
|
|||
|
// scoPacket->data = btdm_malloc(120);
|
|||
|
|
|||
|
ME_InitHandler(&securityHandler);
|
|||
|
securityHandler.callback = security_event_handler;
|
|||
|
SEC_RegisterPairingHandler(&securityHandler);
|
|||
|
SEC_RegisterAuthorizeHandler(&securityHandler);
|
|||
|
|
|||
|
/* Set our promiscuous link policies */
|
|||
|
ME_SetDefaultLinkPolicy(((BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE)&(~BLP_MASK)),
|
|||
|
((BLP_MASTER_SLAVE_SWITCH|BLP_SNIFF_MODE)&(~BLP_MASK)));
|
|||
|
#if BT_ADDR_RANDOM_ENABLE
|
|||
|
if(flashdb_get(FDB_KEY_USER_RANDOM_SEED, (void *)&rand_num[0], 4) != 0){
|
|||
|
default_name[7] = hex_2_char[(rand_num[1]&0xf0) >> 4];
|
|||
|
default_name[8] = hex_2_char[(rand_num[1]&0x0f)];
|
|||
|
default_name[9] = hex_2_char[(rand_num[0]&0xf0) >> 4];
|
|||
|
default_name[10] = hex_2_char[(rand_num[0]&0x0f)];
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
ME_SetLocalDeviceName((const uint8_t *)default_name, sizeof(default_name));
|
|||
|
memset(&eir_data[0],0,EIR_DATA_SIZE);
|
|||
|
eir_data[index++] = sizeof(default_name) + 1;
|
|||
|
eir_data[index++] = 0x09;
|
|||
|
memcpy(&(eir_data[index]),default_name,sizeof(default_name));
|
|||
|
ME_SetExtInquiryRsp(0x01,eir_data,sizeof(default_name)+1);
|
|||
|
|
|||
|
SEC_SetBondingMode(GENERAL_BONDING);
|
|||
|
SEC_SetIoCapabilities(IO_NO_IO);
|
|||
|
ME_InitHandler(&(globalHandler));
|
|||
|
globalHandler.callback = me_callback;
|
|||
|
ME_RegisterGlobalHandler(&globalHandler);
|
|||
|
// ME_SetEventMask(&globalHandler, (BEM_ACCESSIBLE_CHANGE |
|
|||
|
// BEM_LINK_CONNECT_CNF |
|
|||
|
// BEM_LINK_CONNECT_IND |
|
|||
|
// BEM_LINK_DISCONNECT |
|
|||
|
// BEM_ROLE_CHANGE |
|
|||
|
// BEM_MODE_CHANGE |
|
|||
|
// BEM_INQUIRY_COMPLETE |
|
|||
|
// BEM_INQUIRY_RESULT |
|
|||
|
// BEM_INQUIRY_CANCELED));
|
|||
|
ME_SetEventMask(&globalHandler, BEM_ALL_EVENTS);
|
|||
|
|
|||
|
secParms.level = (BSL_SECURITY_L2_IN | BSL_SECURITY_L2_OUT);//HF_SECURITY_SETTINGS;
|
|||
|
secParms.pinLen = 4;
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_HF
|
|||
|
HF_SetSupportedFeature(HF_SDK_FEATURES);
|
|||
|
for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
|
|||
|
HF_RegisterSec(&hf_channel[i], hf_callback, &secParms);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
ME_SetClassOfDevice( COD_AUDIO
|
|||
|
| COD_MAJOR_AUDIO
|
|||
|
| COD_MINOR_AUDIO_HANDSFREE);
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_AG
|
|||
|
HFG_SetSupportedFeature(HFG_SDK_FEATURES);
|
|||
|
for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
|
|||
|
HFG_RegisterSec(&hfg_channel[i], hfg_callback, &secParms);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_A2DP_SNK
|
|||
|
/* Register SBC stream sink */
|
|||
|
sbcSnkCodec1.codecType = AVDTP_CODEC_TYPE_SBC;
|
|||
|
sbcSnkCodec1.elemLen = sizeof(SbcSnkElements);
|
|||
|
sbcSnkCodec1.elements = SbcSnkElements;
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_AAC
|
|||
|
aacSnkCodec1.codecType = AVDTP_CODEC_TYPE_MPEG2_4_AAC;
|
|||
|
aacSnkCodec1.elemLen = sizeof(AacSnkElements);
|
|||
|
aacSnkCodec1.elements = AacSnkElements;
|
|||
|
#else
|
|||
|
sbcSnkCodec2.codecType = AVDTP_CODEC_TYPE_SBC;
|
|||
|
sbcSnkCodec2.elemLen = sizeof(SbcSnkElements);
|
|||
|
sbcSnkCodec2.elements = SbcSnkElements;
|
|||
|
#endif
|
|||
|
|
|||
|
Stream[0].type = A2DP_STREAM_TYPE_SINK;
|
|||
|
Stream[1].type = A2DP_STREAM_TYPE_SINK;
|
|||
|
A2DP_Register(&Stream[0], &sbcSnkCodec1, a2dp_callback);
|
|||
|
#if BTDM_STACK_ENABLE_AAC
|
|||
|
A2DP_Register(&Stream[1], &aacSnkCodec1, a2dp_callback);
|
|||
|
#else
|
|||
|
A2DP_Register(&Stream[1], &sbcSnkCodec2, a2dp_callback);
|
|||
|
#endif
|
|||
|
#endif
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_A2DP_SRC
|
|||
|
/* Register SBC stream src */
|
|||
|
sbcSrcCodec1.codecType = AVDTP_CODEC_TYPE_SBC;
|
|||
|
sbcSrcCodec1.elemLen = sizeof(SbcSrcElements);
|
|||
|
sbcSrcCodec1.elements = SbcSrcElements;
|
|||
|
Stream[2].type = A2DP_STREAM_TYPE_SOURCE;
|
|||
|
Stream[3].type = A2DP_STREAM_TYPE_SOURCE;
|
|||
|
A2DP_Register(&Stream[2], &sbcSrcCodec1, a2dp_callback);
|
|||
|
A2DP_Register(&Stream[3], &sbcSrcCodec1, a2dp_callback);
|
|||
|
#endif
|
|||
|
|
|||
|
// AVDEV_ListenSignalLink(a2dp_device_callback);
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_AVRCP || BTDM_STACK_ENABLE_A2DP_SNK || BTDM_STACK_ENABLE_A2DP_SRC
|
|||
|
for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
|
|||
|
AVRCP_RegisterSec(&rcpCtChannel[i], avrcp_callback, AVRCP_CT, &secParms);
|
|||
|
#if AVRCP_ADVANCED_TARGET == XA_ENABLED
|
|||
|
AVRCP_TgSetEventMask(&rcpCtChannel[i], AVRCP_ENABLE_VOLUME_CHANGED);
|
|||
|
#endif
|
|||
|
AVRCP_RegisterSec(&rcpTgChannel[i], avrcp_callback, AVRCP_TG, &secParms);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_PBAP
|
|||
|
for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
|
|||
|
PBAP_RegisterClientSec(&pbap_client[i], pbap_callback, &secParms);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
#if BTDM_STACK_ENABLE_SPP
|
|||
|
for (uint8_t i=0; i<NUM_BT_DEVICES; i++) {
|
|||
|
spp_register(&spp_dev[i], &secParms, SPP_SERVER_PORT, spp_callback);
|
|||
|
}
|
|||
|
#endif
|
|||
|
ME_SetAccessibleModeC(BAM_NOT_ACCESSIBLE, &access_mode_nc);
|
|||
|
ME_SetAccessibleModeNC(BAM_GENERAL_ACCESSIBLE, &access_mode_nc);
|
|||
|
}
|
|||
|
|