MXC-A36-Demo/MCU/examples/application/btdm_audio/Src/user_bt.c

1168 lines
39 KiB
C
Raw Normal View History

#include <stdint.h>
#include <stdbool.h>
#include "user_bt.h"
#include "assert.h"
#include "app_task.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "btdm_mem.h"
#define MAX_RECONNECT_TIMES 3 //reconnect times if page timeout or linkloss
#define PAGE_TIMEOUT 0x1000 //connect timeout value, uint:625us
const uint8_t hf_enable_voltage_notify[] = "AT+XAPL=AAAA-1111-01,10";
const uint8_t hf_search_remote_dev_type[] = "AT+CGMI";
struct user_bt_env_t user_bt_env;
static TimerHandle_t bt_connect_timer = NULL;
uint8_t bt_get_free_dev(void)
{
uint8_t index;
for(index = 0; index < NUM_BT_DEVICES; index++){
if(user_bt_env.dev[index].state == BT_LINK_STATE_IDLE){
break;
}
}
return index;
}
uint8_t bt_find_dev_index(BD_ADDR *addr)
{
uint8_t index;
printf("find dev :%x,%x,,%x\r\n",addr->A[0],addr->A[1],user_bt_env.dev[0].remote_bd.A[0]);
for(index = 0; index < NUM_BT_DEVICES; index++){
if(memcmp(&user_bt_env.dev[index].remote_bd, addr, sizeof(BD_ADDR)) == 0){
break;
}
}
return index;
}
uint8_t bt_find_dev_index_by_dev(void *dev)
{
uint8_t index;
if(dev == NULL){
return NUM_BT_DEVICES;
}
for(index = 0; index < NUM_BT_DEVICES; index++){
if(user_bt_env.dev[index].remDev == dev){
break;
}
}
return index;
}
uint8_t bt_find_dev_index_by_hfchan(HfChannel *chan)
{
uint8_t index;
if(chan == NULL){
return NUM_BT_DEVICES;
}
for(index = 0; index < NUM_BT_DEVICES; index++){
if(user_bt_env.dev[index].hf_chan == chan){
break;
}
}
return index;
}
uint8_t bt_find_dev_index_by_rcpchan(AvrcpChannel *chan)
{
uint8_t index;
if(chan == NULL){
return NUM_BT_DEVICES;
}
for(index = 0; index < NUM_BT_DEVICES; index++){
if(user_bt_env.dev[index].rcp_chan == chan){
break;
}
}
return index;
}
uint8_t bt_find_dev_index_by_pbapclient(PbapClientSession *client)
{
uint8_t index;
if(client == NULL){
return NUM_BT_DEVICES;
}
for(index = 0; index < NUM_BT_DEVICES; index++){
if(user_bt_env.dev[index].pbap_client == client){
break;
}
}
return index;
}
BtStatus bt_do_connect(BD_ADDR *addr)
{
BtStatus ret = BT_STATUS_PENDING;
uint8_t index = 0;
printf("bt do connect\r\n");
//connect to remote device depending on the profile
if(user_bt_env.enable_profiles & ENABLE_PROFILE_HF){
index = bt_get_free_hf_channel();
ret = HF_CreateServiceLink(&hf_channel[index], addr);
}
else if(user_bt_env.enable_profiles & ENABLE_PROFILE_HFG){
index = bt_get_free_hfg_channel();
ret = HFG_CreateServiceLink(&hfg_channel[index], addr);
}
else if(user_bt_env.enable_profiles & ENABLE_PROFILE_A2DP){
index = bt_get_free_a2dp_stream();
ret = A2DP_OpenStream(&Stream[index],addr);
}
else if(user_bt_env.enable_profiles & ENABLE_PROFILE_HID){
}
else if(user_bt_env.enable_profiles & ENABLE_PROFILE_SPP){
}
printf("ret = %d\r\n",ret);
return ret;
}
void bt_do_disconnect(BD_ADDR *addr, uint8_t force)
{
uint8_t dev_index;
BtStatus status;
dev_index = bt_find_dev_index(addr);
xTimerStop(bt_connect_timer,portMAX_DELAY);
if(force == true){
if(user_bt_env.dev[dev_index].remDev != NULL){
ME_ForceDisconnectLinkWithReason(NULL,user_bt_env.dev[dev_index].remDev,BEC_USER_TERMINATED,TRUE);
}
else{
ME_ForceCancelCreateLink();
}
}
else{
if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_SCO_CONNECTED){
HF_DisconnectAudioLink(user_bt_env.dev[dev_index].hf_chan);
}
if(user_bt_env.dev[dev_index].hf_chan->state != HF_STATE_CLOSED){
status = HF_DisconnectServiceLink(user_bt_env.dev[dev_index].hf_chan);
}
if(user_bt_env.dev[dev_index].hfg_chan->state != HFG_STATE_CLOSED){
HFG_DisconnectServiceLink(user_bt_env.dev[dev_index].hfg_chan);
}
if(user_bt_env.dev[dev_index].rcp_chan->chnl.conn.state != 0){
AVRCP_Disconnect(user_bt_env.dev[dev_index].rcp_chan);
}
if(user_bt_env.dev[dev_index].pstream->stream.state != 0){
A2DP_CloseStream(user_bt_env.dev[dev_index].pstream);
}
}
}
//BtStatus bt_connect_internal(uint8_t dev_index)
//{
// BtStatus status;
// if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING){
// if(user_bt_env.connect_param.connect_times > 0){
// user_bt_env.connect_param.connect_times --;
// status = bt_connect_according_to_profile(&user_bt_env.dev[dev_index].remote_bd,user_bt_env.enable_profiles);
// }
// else{
// status = BT_STATUS_TIMEOUT;
// }
// }
// else{
// status = BT_STATUS_INVALID_PARM;
// }
// printf("connect internal %d,%d\r\n",user_bt_env.connect_param.connect_times,status);
// if(status != BT_STATUS_PENDING){
// user_bt_env.connect_param.connect_times = 0;
// user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
// memset(&user_bt_env.dev[dev_index].remote_bd,0,sizeof(BD_ADDR));
// }
// return status;
//}
void bt_disconnect_cb(BD_ADDR *addr, uint8_t errcode)
{
BtStatus status;
if(errcode == BEC_CONNECTION_TIMEOUT){
//linkloss, auto reconnect in hf mode
// user_bt_env.connect_times = MAX_RECONNECT_TIMES;
// status = bt_connect(addr);
user_bt_env.connect_times = MAX_RECONNECT_TIMES;
memcpy(&user_bt_env.last_dev_addr,addr,BD_ADDR_SIZE);
xTimerStart(bt_connect_timer,portMAX_DELAY);
}
else if(errcode == BEC_USER_TERMINATED){
}
else if(errcode == BEC_LOCAL_TERMINATED){
}
else{
}
}
void bt_connect_timer_cb(TimerHandle_t pxTimer)
{
user_bt_env.connect_times --;
bt_connect(&user_bt_env.last_dev_addr);
}
void bt_connect_cb(uint8_t type, BD_ADDR *addr, uint8_t errcode)
{
// printf("bt connect cb:type = %d,addr=0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,err=%d\r\n",\
// type,addr->A[0],addr->A[1],addr->A[2],addr->A[3],addr->A[4],addr->A[5],errcode);
if((type == BT_EVENT_CON_CNF) && (errcode == BEC_PAGE_TIMEOUT) && (user_bt_env.connect_times > 0)){
memcpy(&user_bt_env.last_dev_addr,addr,BD_ADDR_SIZE);
xTimerStart(bt_connect_timer,portMAX_DELAY);
}
}
static void bt_free_elt(void)
{
struct bt_connect_elt_t *elt;
elt = (struct bt_connect_elt_t *)co_list_pop_front(&user_bt_env.op_list);
if(elt){
user_bt_env.cur_action = BT_ACTION_NULL;
user_bt_env.action_cnt --;
btdm_free((void *)elt);
}
}
uint8_t bt_check_conn(uint8_t dev_index)
{
uint16_t conn_flag = 0;
uint8_t ret = false;
if(user_bt_env.enable_profiles & ENABLE_PROFILE_HF){
conn_flag |= LINK_STATUS_HF_CONNECTED;
}
if(user_bt_env.enable_profiles & ENABLE_PROFILE_A2DP){
conn_flag |= LINK_STATUS_AV_CONNECTED | LINK_STATUS_AVC_CONNECTED;
}
if(user_bt_env.enable_profiles & ENABLE_PROFILE_HFG){
conn_flag |= LINK_STATUS_HFG_CONNECTED;
}
if((user_bt_env.dev[dev_index].conFlags & conn_flag) == conn_flag){
ret = true;
}
printf("bt check conn flag = %x,profile=%x,conn_flg=%x,ret=%d\r\n",user_bt_env.dev[dev_index].conFlags,user_bt_env.enable_profiles,conn_flag,ret);
return ret;
}
void bt_update_conn_status(uint8_t event, void *chan, const void *param)
{
uint8_t dev_index;
BD_ADDR addr;
BtRemoteDevice *rm_dev = NULL;
BtStatus status;
//HfCallbackParms *info;
if(event < BT_PROFILE_HF_MAX){
// info = (HfCallbackParms *)param;
if((event == BT_PROFILE_HF_CONN_REQ) || (event == BT_PROFILE_HF_CONN)){
rm_dev = ((HfCallbackParms *)param)->p.remDev;
dev_index = bt_find_dev_index_by_dev(rm_dev);
}
else{
dev_index = bt_find_dev_index_by_hfchan(chan);
}
}
else if(event < BT_PROFILE_A2DP_MAX){
if(event == BT_PROFILE_A2DP_DISCONN){
A2dpCallbackParms *pm = (A2dpCallbackParms *)param;
rm_dev = pm->p.device;
if(rm_dev == NULL){
rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
}
dev_index = bt_find_dev_index_by_dev(rm_dev);
}
else{
rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
dev_index = bt_find_dev_index_by_dev(rm_dev);
}
}
else if(event < BT_PROFILE_AVRCP_MAX){
if((event == BT_PROFILE_AVRCP_CONN) || (event == BT_PROFILE_AVRCP_DISCONN)){
rm_dev = ((AvrcpCallbackParms *)param)->p.remDev;
dev_index = bt_find_dev_index_by_dev(rm_dev);
}
else{
dev_index = bt_find_dev_index_by_rcpchan(chan);
}
}
else if(event < BT_PROFILE_PBAP_MAX){
dev_index = bt_find_dev_index_by_pbapclient(((PbapClientCallbackParms *)param)->client);
}
if(dev_index >= NUM_BT_DEVICES){
printf("!!!!error dev index, event = %d,%x,%x\r\n",event,(unsigned int)rm_dev,(unsigned int)user_bt_env.dev[0].remDev);
return;
}
printf("bt update conn %d\r\n",event);
switch(event){
case BT_PROFILE_HF_CONN_REQ:
user_bt_env.dev[dev_index].responder = TRUE;
break;
case BT_PROFILE_HF_CONN:
{
user_bt_env.dev[dev_index].hf_chan = chan;
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_HF_CONNECTED;
///check connection
if(true == bt_check_conn(dev_index)){
bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
}
///enable voltage notify
bt_send_hf_cmd(dev_index,hf_enable_voltage_notify);
///search remote device type
bt_send_hf_cmd(dev_index,hf_search_remote_dev_type);
///connect a2dp in master role
if((user_bt_env.dev[dev_index].responder == FALSE)
&& ((user_bt_env.dev[dev_index].conFlags & LINK_STATUS_AV_CONNECTED) == 0)
&& (user_bt_env.enable_profiles & ENABLE_PROFILE_A2DP))
{
uint8_t index = bt_get_free_a2dp_stream();
status = A2DP_OpenStream(&Stream[index],&user_bt_env.dev[dev_index].remote_bd);
}
}
break;
case BT_PROFILE_HF_DISCONN:
{
// ME_GetBdAddr(info->p.remDev, addr);
// dev_index = bt_find_dev_index(addr);
// HfCallbackParms *info = (HfCallbackParms *)param;
// dev_index = bt_find_dev_index_by_dev(info->p.remDev);
user_bt_env.dev[dev_index].hf_chan = NULL;
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_HF_CONNECTED;
}
break;
case BT_PROFILE_HF_CALL:
{
user_bt_env.dev[dev_index].active = ((HfCallbackParms *)param)->p.call;
}
break;
case BT_PROFILE_HF_CALLSETUP:
{
user_bt_env.dev[dev_index].setup_state =((HfCallbackParms *)param)->p.callSetup;
}
break;
case BT_PROFILE_HF_AUDIO_CONN:
{
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_SCO_CONNECTED;
}
break;
case BT_PROFILE_HF_AUDIO_DISCONN:
{
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_SCO_CONNECTED;
}
break;
case BT_PROFILE_A2DP_OPEN_IND:
{
user_bt_env.dev[dev_index].responder = TRUE;
}
break;
case BT_PROFILE_A2DP_CONN:
{
// rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
// dev_index = bt_find_dev_index_by_dev(rm_dev);
// if(dev_index < NUM_BT_DEVICES){
user_bt_env.dev[dev_index].pstream = chan;
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_AV_CONNECTED;
if(true == bt_check_conn(dev_index)){
bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
}
if(((user_bt_env.dev[dev_index].conFlags&LINK_STATUS_AVC_CONNECTED) == 0)
&& (user_bt_env.dev[dev_index].responder == FALSE)){
uint8_t index = bt_get_free_avrcp_channel();
AVRCP_Connect(&rcpCtChannel[index],&user_bt_env.dev[dev_index].remote_bd);
}
// }
}
break;
case BT_PROFILE_A2DP_DISCONN:
{
// A2dpCallbackParms *pm = (A2dpCallbackParms *)param;
// rm_dev = pm->p.device;
// if(rm_dev == NULL){
// rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
// }
// dev_index = bt_find_dev_index_by_dev(rm_dev);
user_bt_env.dev[dev_index].pstream = NULL;
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_AV_CONNECTED;
}
break;
case BT_PROFILE_A2DP_PLAYING:
{
// rm_dev = A2DP_GetRemoteDevice((A2dpStream *)chan);
// dev_index = bt_find_dev_index_by_dev(rm_dev);
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_MEDIA_PLAYING;
}
break;
case BT_PROFILE_A2DP_SUSPEND:
{
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_MEDIA_PLAYING;
}
break;
case BT_PROFILE_AVRCP_CONN:
{
// AvrcpCallbackParms *pm = (AvrcpCallbackParms *)param;
//// ME_GetBdAddr(pm->p.remDev, addr);
//// dev_index = bt_find_dev_index(addr);
// dev_index = bt_find_dev_index_by_dev(pm->p.remDev);
user_bt_env.dev[dev_index].rcp_chan = chan;
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_AVC_CONNECTED;
if(true == bt_check_conn(dev_index)){
bt_connect_act_cmp(BT_EVENT_PROFILE_CONNECT,0,rm_dev);
}
}
break;
case BT_PROFILE_AVRCP_DISCONN:
{
// AvrcpCallbackParms *pm = (AvrcpCallbackParms *)param;
//// ME_GetBdAddr(pm->p.remDev, addr);
//// dev_index = bt_find_dev_index(addr);
// dev_index = bt_find_dev_index_by_dev(pm->p.remDev);
user_bt_env.dev[dev_index].rcp_chan = NULL;
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_AVC_CONNECTED;
}
break;
case BT_PROFILE_AVRCP_MEDIA_STATUS:
{
uint8_t *media_status = (uint8_t *)param;
if((*media_status == AVRCP_MEDIA_PAUSED) || (*media_status == AVRCP_MEDIA_STOPPED)){
printf("media paused\r\n");
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_MEDIA_PLAYING;
}
else{
printf("media playing\r\n");
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_MEDIA_PLAYING;
}
}
break;
case BT_PROFILE_PBAP_CONN:
{
user_bt_env.dev[dev_index].conFlags |= LINK_STATUS_PBAP_CONNECTED;
}
break;
case BT_PROFILE_PBAP_DISCONN:
{
user_bt_env.dev[dev_index].pbap_client = NULL;
user_bt_env.dev[dev_index].conFlags &= ~LINK_STATUS_PBAP_CONNECTED;
}
break;
case BT_PROFILE_PBAP_DATA_IND:
{
printf("data ind: \r\n");
for(uint16_t i = 0; i < ((PbapClientCallbackParms *)param)->u.dataInd.len; i++)
{
printf("%c",((PbapClientCallbackParms *)param)->u.dataInd.buffer[i]);
}
printf("\r\n");
}
break;
case BT_PROFILE_PBAP_COMP:
break;
}
}
void bt_connect_act_cmp(uint8_t evt,uint8_t error,BtRemoteDevice *rem_dev)
{
uint8_t dev_index;
struct bt_connect_elt_t *elt;
uint8_t cur_act = BT_ACTION_NULL;
uint8_t check_op_list = false;
BtStatus ret;
elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
if(elt){
cur_act = elt->action;
}
// printf("bt_connect_act_cmp:evt=%d,act = %d,error=%d\r\n",evt,elt->action,error);
switch(evt){
case BT_EVENT_CON_IND:
case BT_EVENT_CON_CNF:
dev_index = bt_find_dev_index(&rem_dev->bdAddr);
if(dev_index >= NUM_BT_DEVICES){
if(evt == BT_EVENT_CON_IND){
dev_index = bt_get_free_dev();
assert(dev_index < NUM_BT_DEVICES);
}
else{
printf("shall not be here...,%s,%d\r\n",__FUNCTION__,__LINE__);
}
}
if(user_bt_env.bt_connect_cb){
user_bt_env.bt_connect_cb(evt,&rem_dev->bdAddr, error);
}
if(error == BEC_NO_ERROR){
user_bt_env.dev[dev_index].remDev = rem_dev;
user_bt_env.dev[dev_index].state = BT_LINK_STATE_ACL_CONNECTED;
user_bt_env.last_active_index = dev_index;
memcpy(&user_bt_env.dev[dev_index].remote_bd,&rem_dev->bdAddr,BD_ADDR_SIZE);
}
else{
user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
memset(&user_bt_env.dev[dev_index].remote_bd,0,BD_ADDR_SIZE);
if((cur_act == BT_ACTION_CONNECT) || (cur_act == BT_ACTION_DISCONNECT)){
bt_free_elt();
check_op_list = true;
}
}
break;
case BT_EVENT_PROFILE_CONNECT:
dev_index = bt_find_dev_index(&rem_dev->bdAddr);
user_bt_env.dev[dev_index].state = BT_LINK_STATE_PROFILE_CONNECTED;
printf("\r\nIV\r\n");
if(user_bt_env.bt_connect_cb){
user_bt_env.bt_connect_cb(evt,&rem_dev->bdAddr, error);
}
if(cur_act == BT_ACTION_CONNECT){
bt_free_elt();
check_op_list = true;
}
break;
case BT_EVENT_DISCONNECT:
dev_index = bt_find_dev_index(&rem_dev->bdAddr);
user_bt_env.dev[dev_index].state = BT_LINK_STATE_IDLE;
if(user_bt_env.bt_disconnect_cb){
user_bt_env.bt_disconnect_cb(&rem_dev->bdAddr, error);
}
if((cur_act == BT_ACTION_DISCONNECT) || (cur_act == BT_ACTION_CONNECT)){
bt_free_elt();
check_op_list = true;
}
// memset(&user_bt_env.dev[dev_index].remote_bd,0,BD_ADDR_SIZE);
// user_bt_env.dev[dev_index].conFlags = 0;
// user_bt_env.dev[dev_index].hfg_chan = NULL;
// user_bt_env.dev[dev_index].hf_chan = NULL;
// user_bt_env.dev[dev_index].pstream = NULL;
// user_bt_env.dev[dev_index].rcp_chan = NULL;
// user_bt_env.dev[dev_index].hid_chan = NULL;
memset(&user_bt_env.dev[dev_index],0,sizeof(APP_DEVICE));
break;
case BT_EVENT_ACC_CHG:
if(user_bt_env.bt_access_change_cb){
user_bt_env.bt_access_change_cb(error);
}
user_bt_env.access_state = error;
if(cur_act == BT_ACTION_ACCESS){
bt_free_elt();
check_op_list = true;
}
break;
}
///check pending op list
if(check_op_list == true){
elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
while(elt && (check_op_list == true))
{
printf("check pending list act=%d,state=%d,addr0=%x\r\n",elt->action,user_bt_env.dev[0].state,user_bt_env.dev[0].remote_bd.A[0]);
switch(elt->action){
case BT_ACTION_CONNECT:
{
dev_index = bt_find_dev_index(&elt->addr);
if(dev_index >= NUM_BT_DEVICES){
//device not exist, find free dev
dev_index = bt_get_free_dev();
if(dev_index < NUM_BT_DEVICES){
//get free dev, create new connection
ret = bt_do_connect(&elt->addr);
if(ret == BT_STATUS_PENDING){
user_bt_env.cur_action = BT_ACTION_CONNECT;
user_bt_env.dev[dev_index].state = BT_LINK_STATE_CONNECTING;
memcpy(&user_bt_env.dev[dev_index].remote_bd,&elt->addr,sizeof(BD_ADDR));
check_op_list = false;
}
else{
bt_free_elt();
}
}
else{
//no free dev
bt_free_elt();
}
}
else{
//device exist,check device state
if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
//profile connected,return success
bt_free_elt();
}else{
check_op_list = false;
}
}
}
break;
case BT_ACTION_DISCONNECT:
{
dev_index = bt_find_dev_index(&elt->addr);
if(dev_index >= NUM_BT_DEVICES){
//don't find device
bt_free_elt();
}
else{
if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
//profile connected,return success
user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
user_bt_env.cur_action = BT_ACTION_DISCONNECT;
bt_do_disconnect(&elt->addr,false);
}
else{
//other state,just return
check_op_list = false;
}
}
}
break;
case BT_ACTION_ACCESS:
{
printf("shall not be here..error action access change.");
bt_free_elt();
}
break;
}
elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
}
}
}
void bt_access_change_cb(uint8_t mode)
{
printf("access change cb: %d\r\n",mode);
}
BtStatus bt_connect(BD_ADDR *addr)
{
BtStatus ret = BT_STATUS_PENDING;
uint8_t dev_index = 0;
struct bt_connect_elt_t *elt;
uint8_t add_to_list = false;
printf("bt connect: %x,%x\r\n",addr->A[0],addr->A[1]);
//param check
assert(addr != NULL);
// //error bt state, return failed
// for(index = 0; index < NUM_BT_DEVICES; index++){
// if((user_bt_env.dev[index].state == BT_LINK_STATE_CONNECTING) || (user_bt_env.dev[index].state == BT_LINK_STATE_DISCONNECTING)){
// return BT_STATUS_FAILED;
// }
// }
elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
memcpy(&elt->addr,addr,BD_ADDR_SIZE);
elt->action = BT_ACTION_CONNECT;
if(co_list_is_empty(&user_bt_env.op_list)){
//list empty, check device index
dev_index = bt_find_dev_index(addr);
if(dev_index >= NUM_BT_DEVICES){
//not connection with the addr
dev_index = bt_get_free_dev();
if(dev_index >= NUM_BT_DEVICES){
//no free device
btdm_free(elt);
return BT_STATUS_NO_RESOURCES;
}
}
if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_IDLE){
//idle state, do connect imediately
ret = bt_do_connect(addr);
if(ret == BT_STATUS_PENDING){
add_to_list = true;
user_bt_env.cur_action = BT_ACTION_CONNECT;
user_bt_env.dev[dev_index].state = BT_LINK_STATE_CONNECTING;
memcpy(&user_bt_env.dev[dev_index].remote_bd,addr,sizeof(BD_ADDR));
}
else{
btdm_free(elt);
}
}
else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_DISCONNECTING){
//disconnecting,usally shall not be here
add_to_list = true;
}
else if((user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING)
|| (user_bt_env.dev[dev_index].state == BT_LINK_STATE_ACL_CONNECTED)){
//connecting or acl connected,just free connect elt,return pending
btdm_free(elt);
}
else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
//profile connected,return success
btdm_free(elt);
ret = BT_STATUS_SUCCESS;
}
}else{
add_to_list = true;
}
if(add_to_list == true){
GLOBAL_INT_DISABLE();
co_list_push_back(&user_bt_env.op_list,&elt->hdr);
user_bt_env.action_cnt ++;
GLOBAL_INT_RESTORE();
}
return ret;
}
BtStatus bt_disconnect(BD_ADDR *addr, uint8_t force_disconnect)
{
BtStatus status = BT_STATUS_PENDING;
uint8_t dev_index = 0;
struct bt_connect_elt_t *elt,*cur_elt;
uint8_t add_to_list = false;
printf("bt disconnect\r\n");
elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
memcpy(&elt->addr,addr,BD_ADDR_SIZE);
elt->action = BT_ACTION_DISCONNECT;
if((co_list_is_empty(&user_bt_env.op_list)) || (force_disconnect == true)){
dev_index = bt_find_dev_index(addr);
if(dev_index < NUM_BT_DEVICES){
if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_IDLE){
memset(&user_bt_env.dev[dev_index].remote_bd,0,sizeof(BD_ADDR));
status = BT_STATUS_SUCCESS;
}
else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_DISCONNECTING){
add_to_list = false;
if(force_disconnect == true){
// cur_elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
bt_do_disconnect(addr,force_disconnect);
}
}
else if((user_bt_env.dev[dev_index].state == BT_LINK_STATE_CONNECTING)
|| (user_bt_env.dev[dev_index].state == BT_LINK_STATE_ACL_CONNECTED))
{
if(force_disconnect == true){
cur_elt = (struct bt_connect_elt_t *)co_list_pick(&user_bt_env.op_list);
if(cur_elt && (cur_elt->action == BT_ACTION_CONNECT)){
co_list_pop_front(&user_bt_env.op_list);
user_bt_env.action_cnt --;
btdm_free(cur_elt);
user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
bt_do_disconnect(addr,force_disconnect);
}
}
add_to_list = true;
}
else if(user_bt_env.dev[dev_index].state == BT_LINK_STATE_PROFILE_CONNECTED){
user_bt_env.dev[dev_index].state = BT_LINK_STATE_DISCONNECTING;
bt_do_disconnect(addr,force_disconnect);
add_to_list = true;
}
}
else{
btdm_free(elt);
return BT_STATUS_FAILED;
}
}
else{
add_to_list = true;
}
if(add_to_list == true){
GLOBAL_INT_DISABLE();
co_list_push_back(&user_bt_env.op_list,&elt->hdr);
user_bt_env.action_cnt ++;
GLOBAL_INT_RESTORE();
}
return status;
}
void user_bt_init(void)
{
struct bt_connect_elt_t *elt;
while(!co_list_is_empty(&user_bt_env.op_list)){
user_bt_env.action_cnt --;
elt = (struct bt_connect_elt_t *)co_list_pop_front(&user_bt_env.op_list);
btdm_free((void *)elt);
}
memset((void *)&user_bt_env,0,sizeof(struct user_bt_env_t));
co_list_init(&user_bt_env.op_list);
user_bt_env.bt_disconnect_cb = bt_disconnect_cb;
user_bt_env.bt_connect_cb = bt_connect_cb;
user_bt_env.bt_access_change_cb = bt_access_change_cb;
user_bt_env.enable_profiles = ENABLE_PROFILE_HF | ENABLE_PROFILE_A2DP;
// user_bt_env.connect_times = MAX_RECONNECT_TIMES;
user_bt_env.page_timeout = PAGE_TIMEOUT;
ME_SetPageTimeout(user_bt_env.page_timeout);
if(bt_connect_timer == NULL){
bt_connect_timer = xTimerCreate("bt_connect_timer", 10, pdFALSE, 0, bt_connect_timer_cb );
}
}
uint8_t user_bt_get_state(uint8_t dev_index)
{
uint8_t state = BT_STATE_IDLE;
switch(user_bt_env.dev[dev_index].state)
{
case BT_LINK_STATE_IDLE:
state = BT_STATE_IDLE;
break;
case BT_LINK_STATE_CONNECTING:
state = BT_STATE_CONNECTING;
break;
case BT_LINK_STATE_DISCONNECTING:
state = BT_STATE_DISCONNECTING;
break;
case BT_LINK_STATE_ACL_CONNECTED:
case BT_LINK_STATE_PROFILE_CONNECTED:
{
state = BT_STATE_CONNECTED;
if(user_bt_env.dev[dev_index].active == HF_CALL_ACTIVE){
state = BT_STATE_HFP_CALLACTIVE;
}
else if(user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_IN){
state = BT_STATE_HFP_INCOMMING;
}
else if((user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_OUT)
||(user_bt_env.dev[dev_index].setup_state == HF_CALL_SETUP_ALERT)){
state = BT_STATE_HFP_OUTGOING;
}else if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_MEDIA_PLAYING){
state = BT_STATE_MEDIA_PLAYING;
}
}
break;
}
return state;
}
BtStatus bt_enter_pairing(uint8_t access, BtAccessModeInfo *info)
{
BtStatus status = BT_STATUS_PENDING;
struct bt_connect_elt_t *elt;
printf("bt enter pairing\r\n");
if((access == BAM_NOT_ACCESSIBLE) || (access > BAM_GENERAL_ACCESSIBLE)){
return BT_STATUS_INVALID_PARM;
}
if(user_bt_env.access_state == access){
return BT_STATUS_SUCCESS;
}
//TBD,only change nonconnected access state
status = ME_SetAccessibleModeNC(access, info);
if(status == BT_STATUS_PENDING){
elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
elt->action = BT_ACTION_ACCESS;
GLOBAL_INT_DISABLE();
co_list_push_back(&user_bt_env.op_list,&elt->hdr);
user_bt_env.action_cnt ++;
GLOBAL_INT_RESTORE();
}
return status;
}
BtStatus bt_exit_pairing(void)
{
BtStatus status = BT_STATUS_PENDING;
struct bt_connect_elt_t *elt;
status = ME_SetAccessibleModeNC(BAM_NOT_ACCESSIBLE, NULL);
if(status == BT_STATUS_PENDING){
elt = (struct bt_connect_elt_t *)btdm_malloc(sizeof(struct bt_connect_elt_t));
elt->action = BT_ACTION_ACCESS;
GLOBAL_INT_DISABLE();
co_list_push_back(&user_bt_env.op_list,&elt->hdr);
user_bt_env.action_cnt ++;
GLOBAL_INT_RESTORE();
}
return status;
}
BtStatus bt_answer_call(uint8_t dev_index)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
if(state != BT_STATE_HFP_INCOMMING){
return BT_STATUS_FAILED;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_AnswerCall(user_bt_env.dev[dev_index].hf_chan, cmd);
}
BtStatus bt_dial_number(uint8_t dev_index, uint8_t *number, uint16_t len)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_DialNumber(user_bt_env.dev[dev_index].hf_chan, number, len, cmd);
}
BtStatus bt_redial(uint8_t dev_index)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_Redial(user_bt_env.dev[dev_index].hf_chan, cmd);
}
BtStatus bt_call_hold(uint8_t dev_index, HfHoldAction action, uint8_t index)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_CallHold(user_bt_env.dev[dev_index].hf_chan, action, index, cmd);
}
BtStatus bt_hang_up(uint8_t dev_index)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
printf("state = %d\r\n",state);
if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
return BT_STATUS_FAILED;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_Hangup(user_bt_env.dev[dev_index].hf_chan, cmd);
}
BtStatus bt_list_current_calls(uint8_t dev_index)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
printf("state = %d\r\n",state);
if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
return BT_STATUS_FAILED;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_ListCurrentCalls(user_bt_env.dev[dev_index].hf_chan, cmd);
}
BtStatus bt_transfer_sco(uint8_t dev_index)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
if(state < BT_STATE_CONNECTED){
return BT_STATUS_FAILED;
}
printf("conFlags = %d\r\n",user_bt_env.dev[dev_index].conFlags);
if(user_bt_env.dev[dev_index].conFlags & LINK_STATUS_SCO_CONNECTED){
return HF_DisconnectAudioLink(user_bt_env.dev[dev_index].hf_chan);
}else{
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
//return HF_CreateAudioLink(user_bt_env.dev[dev_index].hf_chan);
return HF_CodecConnectionReq(user_bt_env.dev[dev_index].hf_chan, cmd);
}
}
BtStatus bt_send_dtmf(uint8_t dev_index, uint8_t dtmf)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_GenerateDtmf(user_bt_env.dev[dev_index].hf_chan, dtmf, cmd);
}
BtStatus bt_report_mic_volume(uint8_t dev_index, uint8_t vol)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
return BT_STATUS_FAILED;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_ReportMicVolume(user_bt_env.dev[dev_index].hf_chan, vol, cmd);
}
BtStatus bt_report_spk_volume(uint8_t dev_index, uint8_t vol)
{
HfCommand *cmd;
uint8_t state = user_bt_get_state(dev_index);
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
if((state != BT_STATE_HFP_INCOMMING) && (state != BT_STATE_HFP_OUTGOING) && (state != BT_STATE_HFP_CALLACTIVE)){
return BT_STATUS_FAILED;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_ReportSpeakerVolume(user_bt_env.dev[dev_index].hf_chan, vol, cmd);
}
BtStatus bt_send_hf_cmd(uint8_t dev_index, const uint8_t *at_str)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_SendAtCommand(user_bt_env.dev[dev_index].hf_chan, at_str, cmd);
}
BtStatus bt_enable_voice_recognition(uint8_t dev_index, uint8_t enabled)
{
HfCommand *cmd;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
cmd = (HfCommand *)btdm_malloc(sizeof(HfCommand));
return HF_EnableVoiceRecognition(user_bt_env.dev[dev_index].hf_chan, enabled, cmd);
}
uint8_t bt_is_voice_rec_active(uint8_t dev_index)
{
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].hf_chan == NULL)){
return 0;
}
return HF_IsVoiceRecActive(user_bt_env.dev[dev_index].hf_chan);
}
BtStatus bt_set_media_volume(uint8_t dev_index, uint8_t volume)
{
AvrcpAdvancedPdu *pdu;
BtStatus status;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
pdu->parms = (uint8_t *)btdm_malloc(64);
status = AVRCP_TgSetAbsoluteVolume(user_bt_env.dev[dev_index].rcp_chan, pdu,volume);
if (status != BT_STATUS_PENDING) {
btdm_free(pdu->parms);
btdm_free(pdu);
}
return status;
}
BtStatus bt_get_playstatus(uint8_t dev_index)
{
AvrcpAdvancedPdu *pdu;
BtStatus status;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
pdu->parms = (uint8_t *)btdm_malloc(64);
status = AVRCP_CtGetPlayStatus(user_bt_env.dev[dev_index].rcp_chan, pdu);
if (status != BT_STATUS_PENDING) {
btdm_free(pdu->parms);
btdm_free(pdu);
}
return status;
}
BtStatus bt_get_media_info(uint8_t dev_index,AvrcpMediaAttrIdMask mediaMask)
{
AvrcpAdvancedPdu *pdu;
BtStatus status;
if((dev_index >= NUM_BT_DEVICES) || (user_bt_env.dev[dev_index].rcp_chan == NULL)){
return BT_STATUS_INVALID_PARM;
}
pdu = (AvrcpAdvancedPdu *)btdm_malloc(sizeof(AvrcpAdvancedPdu));
pdu->parms = (uint8_t *)btdm_malloc(64);
status = AVRCP_CtGetMediaInfo(user_bt_env.dev[dev_index].rcp_chan, pdu, mediaMask);
if (status != BT_STATUS_PENDING) {
btdm_free(pdu->parms);
btdm_free(pdu);
}
return status;
}