#include #include #include #include #include "btdm_host.h" #include "app_config.h" #include "app_bt.h" #include "user_bt.h" #include "elog.h" #include "user_bt_pbap.h" #include "fdb_app.h" #include "user_bt_a2dp.h" #include "user_bt_call.h" #include "user_bt_common.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: printf("HF_EVENT_SPEAKER_VOLUME %d\r\n",Info->p.gain); user_bt_hf_set_volume(Info->p.gain); break; case HF_EVENT_CURRENT_CALL_STATE: { const char *number = Info->p.callListParms->number; printf("current call:%c,%c,%c,%d\r\n",number[0],number[1],number[2],strlen(number)); user_bt_set_calling_num(number, strlen(number)); call_record_list_parse_save((uint8_t*)number, strlen(number)); } break; case HF_EVENT_AT_RESULT_DATA: { HfAtData *atdata = (HfAtData *)Info->p.data; printf("at result:"); for(uint8_t i = 0; i < atdata->dataLen; i++) { printf("%c",atdata->data[i]); } printf("\r\n"); } 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); user_bt_set_local_volume(BT_VOL_MEDIA, Parms->p.adv.info.volume); } break; case AVRCP_EVENT_ADV_RESPONSE: ///??????TG??????????????? /**/ 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); } } } ///????????????????????? if(Parms->advOp == AVRCP_OP_REGISTER_NOTIFY) { ///????????????????? 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); memset((void *)&bt_media.song[0], 0, 100); if(len0 < 100) { /* limit len, if you open video Will be greater than 100*/ memcpy(&bt_media.song[0], Parms->p.adv.rsp.element.txt[0].string, len0); } printf("song len %d \r\n", len0); #if 1 user_bt_song_lyric_notify(); #endif } 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: { AvrcpAdvNotifyParms *avrcp_ntf = (AvrcpAdvNotifyParms *)&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: Parms->u.dataInd.buffer[Parms->u.dataInd.len] = 0; printf("data ind: \r\n"); for(uint16_t i = 0; i < Parms->u.dataInd.len; i++) { printf("%c",Parms->u.dataInd.buffer[i]); } printf("\r\n"); user_pbap_data_parse(Parms->u.dataInd.buffer,Parms->u.dataInd.len); break; case PBAP_EVENT_TP_CONNECTED: user_set_pbap_state(1); printf("PBAP_EVENT_TP_CONNECTED \r\n"); break; case PBAP_EVENT_TP_DISCONNECTED: user_set_pbap_state(0); printf("PBAP_EVENT_TP_DISCONNECTED \r\n"); 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. */ printf("PBAP_EVENT_ABORTED \r\n"); break; case PBAP_EVENT_COMPLETE: printf("PBAP_EVENT_COMPLETE \r\n"); pbap_data_pull_completed(Parms->oper); 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) { log_i("BTDM STRUCT SIZE: defined in stack.\r\n"); extern void btdm_struct_size_dump(void); btdm_struct_size_dump(); log_i("BTDM STRUCT SIZE: defined in api.\r\n"); log_i("BtEvent is %d\r\n", sizeof(BtEvent)); log_i("BtRemoteDevice is %d\r\n", sizeof(BtRemoteDevice)); log_i("RfDeferEvent is %d\r\n", sizeof(RfDeferEvent)); log_i("RfChannel is %d\r\n", sizeof(RfChannel)); log_i("HfChannel is %d\r\n", sizeof(HfChannel)); log_i("HfCommand is %d\r\n", sizeof(HfCommand)); log_i("HfCallbackParms is %d\r\n", sizeof(HfCallbackParms)); log_i("HfgChannel is %d\r\n", sizeof(HfgChannel)); log_i("HfgCallbackParms is %d\r\n", sizeof(HfgCallbackParms)); log_i("A2dpStream is %d\r\n", sizeof(A2dpStream)); log_i("A2dpCallbackParms is %d\r\n", sizeof(A2dpCallbackParms)); log_i("AvdtpStream is %d\r\n", sizeof(AvdtpStream)); log_i("AvdtpConn is %d\r\n", sizeof(AvdtpConn)); log_i("AvdtpCallbackParms is %d\r\n", sizeof(AvdtpCallbackParms)); log_i("AvctpCallbackParms is %d\r\n", sizeof(AvctpCallbackParms)); log_i("AvctpChannel is %d\r\n", sizeof(AvctpChannel)); log_i("AvctpCmdFrame is %d\r\n", sizeof(AvctpCmdFrame)); log_i("AvctpRspFrame is %d\r\n", sizeof(AvctpRspFrame)); log_i("AvrcpChannel is %d\r\n", sizeof(AvrcpChannel)); log_i("AvrcpCallbackParms is %d\r\n", sizeof(AvrcpCallbackParms)); log_i("AvrcpAdvancedPdu is %d\r\n", sizeof(AvrcpAdvancedPdu)); log_i("AvrcpPlayerSetting is %d\r\n", sizeof(AvrcpPlayerSetting)); log_i("AvrcpPlayerStrings is %d\r\n", sizeof(AvrcpPlayerStrings)); log_i("AvrcpEqString is %d\r\n", sizeof(AvrcpEqString)); log_i("AvrcpRepeatString is %d\r\n", sizeof(AvrcpRepeatString)); log_i("AvrcpShuffleString is %d\r\n", sizeof(AvrcpShuffleString)); log_i("AvrcpScanString is %d\r\n", sizeof(AvrcpScanString)); log_i("AvrcpExtString is %d\r\n", sizeof(AvrcpExtString)); log_i("AvrcpCharSets is %d\r\n", sizeof(AvrcpCharSets)); log_i("AvrcpMediaInfo is %d\r\n", sizeof(AvrcpMediaInfo)); log_i("AvrcpMediaPlayStatus is %d\r\n", sizeof(AvrcpMediaPlayStatus)); log_i("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) { #define BT_DEVICE_NAME "FR30xx_LH" // 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