Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bluetooth: Introducing the AVRCP function. #31

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions framework/api/bt_hfp_ag.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,11 @@ bt_status_t BTSYMBOLS(bt_hfp_ag_send_at_command)(bt_instance_t* ins, bt_address_
hfp_ag_interface_t* profile = get_profile_service();

return profile->send_at_command(addr, at_command);
}

bt_status_t BTSYMBOLS(bt_hfp_ag_send_vendor_specific_at_command)(bt_instance_t* ins, bt_address_t* addr, const char* command, const char* value)
{
hfp_ag_interface_t* profile = get_profile_service();

return profile->send_vendor_specific_at_command(addr, command, value);
}
4 changes: 4 additions & 0 deletions framework/include/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ typedef struct {
#else
#define BT_DEV_NAME_MAX_LEN (64)
#endif

#define BLUETOOTH_COMPANY_ID_XIAOMI 0x038F
#define BLUETOOTH_COMPANY_ID_GOOGLE 0x00E0

#define BT_LOC_NAME_MAX_LEN BT_DEV_NAME_MAX_LEN
#define BT_REM_NAME_MAX_LEN BT_DEV_NAME_MAX_LEN

Expand Down
1 change: 1 addition & 0 deletions framework/include/bt_hfp.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extern "C" {
Also defined as BTA_HF_CLIENT_AT_MAX_LEN (512) at Android */
#define HFP_AT_LEN_MAX 512
#define HFP_CALL_LIST_MAX 4
#define HFP_COMPANY_PREFIX_LEN_MAX 10

typedef enum {
HFP_AUDIO_STATE_DISCONNECTED,
Expand Down
23 changes: 23 additions & 0 deletions framework/include/bt_hfp_ag.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,17 @@ typedef void (*hfp_ag_dial_call_callback)(void* cookie, bt_address_t* addr, cons
*/
typedef void (*hfp_ag_at_cmd_received_callback)(void* cookie, bt_address_t* addr, const char* at_command);

/**
* @brief HFP vendor specific AT command received callback
*
* @param cookie - callback cookie.
* @param command - The prefix of the AT command.
* @param company_id - Bluetooth company ID.
* @param value - AT command value.
* @param addr - address of peer HF device.
*/
typedef void (*hfp_ag_vend_spec_at_cmd_received_callback)(void* cookie, bt_address_t* addr, const char* command, uint16_t company_id, const char* value);

/**
* @brief HFP AG callback structure
*
Expand All @@ -149,6 +160,7 @@ typedef struct
hfp_ag_hangup_call_callback hangup_call_cb;
hfp_ag_dial_call_callback dial_call_cb;
hfp_ag_at_cmd_received_callback at_cmd_cb;
hfp_ag_vend_spec_at_cmd_received_callback vender_specific_at_cmd_cb;
} hfp_ag_callbacks_t;

/**
Expand Down Expand Up @@ -323,6 +335,17 @@ bt_status_t BTSYMBOLS(bt_hfp_ag_volume_control)(bt_instance_t* ins, bt_address_t
* @return bt_status_t - BT_STATUS_SUCCESS on success, a negated errno value on failure.
*/
bt_status_t BTSYMBOLS(bt_hfp_ag_send_at_command)(bt_instance_t* ins, bt_address_t* addr, const char* at_command);

/**
* @brief Send vendor specific AT Command
*
* @param ins - bluetooth client instance.
* @param addr - address of peer HF device.
* @param command - the prefix of the AT command to be send.
* @param value - the value of the AT command to be send.
* @return bt_status_t - BT_STATUS_SUCCESS on success, a negated errno value on failure.
*/
bt_status_t BTSYMBOLS(bt_hfp_ag_send_vendor_specific_at_command)(bt_instance_t* ins, bt_address_t* addr, const char* command, const char* value);
#ifdef __cplusplus
}
#endif
Expand Down
17 changes: 17 additions & 0 deletions framework/socket/bt_hfp_ag.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,20 @@ bt_status_t bt_hfp_ag_send_at_command(bt_instance_t* ins, bt_address_t* addr, co

return packet.hfp_ag_r.status;
}

bt_status_t bt_hfp_ag_send_vendor_specific_at_command(bt_instance_t* ins, bt_address_t* addr, const char* command, const char* value)
{
bt_message_packet_t packet;
bt_status_t status;

BT_SOCKET_INS_VALID(ins, BT_STATUS_PARM_INVALID);

memcpy(&packet.hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.addr, addr, sizeof(bt_address_t));
strlcpy(packet.hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.cmd, command, sizeof(packet.hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.cmd));
strlcpy(packet.hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.value, value, sizeof(packet.hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.value));
status = bt_socket_client_sendrecv(ins, &packet, BT_HFP_AG_SEND_VENDOR_SPECIFIC_AT_COMMAND);
if (status != BT_STATUS_SUCCESS)
return status;

return packet.hfp_ag_r.status;
}
18 changes: 18 additions & 0 deletions service/ipc/socket/include/bt_message_hfp_ag.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ BT_HFP_AG_MESSAGE_START,
BT_HFP_AG_NOTIFY_DEVICE_STATUS,
BT_HFP_AG_VOLUME_CONTROL,
BT_HFP_AG_SEND_AT_COMMAND,
BT_HFP_AG_SEND_VENDOR_SPECIFIC_AT_COMMAND,
BT_HFP_AG_MESSAGE_END,
#endif

Expand All @@ -48,6 +49,7 @@ BT_HFP_AG_MESSAGE_START,
BT_HFP_AG_ON_HANGUP_CALL,
BT_HFP_AG_ON_DIAL_CALL,
BT_HFP_AG_ON_AT_COMMAND_RECEIVED,
BT_HFP_AG_ON_VENDOR_SPECIFIC_AT_COMMAND_RECEIVED,
BT_HFP_AG_CALLBACK_END,
#endif

Expand Down Expand Up @@ -113,6 +115,14 @@ BT_HFP_AG_MESSAGE_START,
uint8_t pad[2];
char cmd[HFP_AT_LEN_MAX + 1];
} _bt_hfp_ag_send_at_cmd;

struct {
bt_address_t addr;
uint8_t pad[2];
char cmd[HFP_COMPANY_PREFIX_LEN_MAX + 1];
uint8_t pad1[(HFP_COMPANY_PREFIX_LEN_MAX + 1 + 3) / 4 * 4 - (HFP_COMPANY_PREFIX_LEN_MAX + 1)];
char value[HFP_AT_LEN_MAX + 1];
} _bt_hfp_ag_send_vendor_specific_at_cmd;
} bt_message_hfp_ag_t;

typedef union {
Expand Down Expand Up @@ -159,6 +169,14 @@ BT_HFP_AG_MESSAGE_START,
uint8_t pad[2];
char cmd[HFP_AT_LEN_MAX + 1];
} _on_at_cmd_received;

struct {
bt_address_t addr;
uint16_t company_id;
char command[HFP_COMPANY_PREFIX_LEN_MAX + 1];
uint8_t pad1[(HFP_COMPANY_PREFIX_LEN_MAX + 1 + 3) / 4 * 4 - (HFP_COMPANY_PREFIX_LEN_MAX + 1)];
char value[HFP_AT_LEN_MAX + 1];
} _on_vend_spec_at_cmd_received;
} bt_message_hfp_ag_callbacks_t;

#ifdef __cplusplus
Expand Down
34 changes: 34 additions & 0 deletions service/ipc/socket/src/bt_socket_hfp_ag.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ static void on_at_cmd_received_cb(void* cookie, bt_address_t* addr, const char*
bt_socket_server_send(ins, &packet, BT_HFP_AG_ON_AT_COMMAND_RECEIVED);
}

static void on_vendor_specific_at_cmd_received_cb(void* cookie, bt_address_t* addr, const char* command, uint16_t company_id, const char* value)
{
bt_message_packet_t packet = { 0 };
bt_instance_t* ins = cookie;

memcpy(&packet.hfp_ag_cb._on_vend_spec_at_cmd_received.addr, addr, sizeof(bt_address_t));
if (command != NULL)
strlcpy(packet.hfp_ag_cb._on_vend_spec_at_cmd_received.command, command,
sizeof(packet.hfp_ag_cb._on_vend_spec_at_cmd_received.command));

packet.hfp_ag_cb._on_vend_spec_at_cmd_received.company_id = company_id;

if (value != NULL)
strlcpy(packet.hfp_ag_cb._on_vend_spec_at_cmd_received.value, value,
sizeof(packet.hfp_ag_cb._on_vend_spec_at_cmd_received.value));

bt_socket_server_send(ins, &packet, BT_HFP_AG_ON_VENDOR_SPECIFIC_AT_COMMAND_RECEIVED);
}

const static hfp_ag_callbacks_t g_hfp_ag_socket_cbs = {
.connection_state_cb = on_connection_state_changed_cb,
.audio_state_cb = on_audio_state_changed_cb,
Expand All @@ -184,6 +203,7 @@ const static hfp_ag_callbacks_t g_hfp_ag_socket_cbs = {
.hangup_call_cb = on_hangup_call_cb,
.dial_call_cb = on_dial_call_cb,
.at_cmd_cb = on_at_cmd_received_cb,
.vender_specific_at_cmd_cb = on_vendor_specific_at_cmd_received_cb,
};

/****************************************************************************
Expand Down Expand Up @@ -297,6 +317,12 @@ void bt_socket_server_hfp_ag_process(service_poll_t* poll, int fd,
&packet->hfp_ag_pl._bt_hfp_ag_send_at_cmd.addr,
packet->hfp_ag_pl._bt_hfp_ag_send_at_cmd.cmd);
break;
case BT_HFP_AG_SEND_VENDOR_SPECIFIC_AT_COMMAND:
packet->hfp_ag_r.status = BTSYMBOLS(bt_hfp_ag_send_vendor_specific_at_command)(ins,
&packet->hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.addr,
packet->hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.cmd,
packet->hfp_ag_pl._bt_hfp_ag_send_vendor_specific_at_cmd.value);
break;
default:
break;
}
Expand Down Expand Up @@ -365,6 +391,14 @@ int bt_socket_client_hfp_ag_callback(service_poll_t* poll,
&packet->hfp_ag_cb._on_at_cmd_received.addr,
packet->hfp_ag_cb._on_at_cmd_received.cmd);
break;
case BT_HFP_AG_ON_VENDOR_SPECIFIC_AT_COMMAND_RECEIVED:
CALLBACK_FOREACH(CBLIST, hfp_ag_callbacks_t,
vender_specific_at_cmd_cb,
&packet->hfp_ag_cb._on_vend_spec_at_cmd_received.addr,
packet->hfp_ag_cb._on_vend_spec_at_cmd_received.command,
packet->hfp_ag_cb._on_vend_spec_at_cmd_received.company_id,
packet->hfp_ag_cb._on_vend_spec_at_cmd_received.value);
break;
default:
return BT_STATUS_PARM_INVALID;
}
Expand Down
2 changes: 1 addition & 1 deletion service/profiles/audio_interface/audio_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ void audio_ctrl_cleanup(uint8_t profile_id)
case PROFILE_HFP_AG:
case PROFILE_HFP_HF:
if (g_audio_ctrl_transport) {
audio_transport_close(g_audio_ctrl_transport, CONFIG_BLUETOOTH_AUDIO_TRANS_ID_HFP_CTRL);
audio_transport_close(g_audio_ctrl_transport, AUDIO_TRANS_CH_ID_ALL);
}
break;
default:
Expand Down
22 changes: 22 additions & 0 deletions service/profiles/hfp_ag/hfp_ag_service.c
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,21 @@ bt_status_t hfp_ag_send_at_command(bt_address_t* addr, const char* at_command)
return hfp_ag_send_message(msg);
}

bt_status_t hfp_ag_send_vendor_specific_at_command(bt_address_t* addr, const char* command, const char* value)
{
if (!command || !value)
return BT_STATUS_PARM_INVALID;

hfp_ag_msg_t* msg = hfp_ag_msg_new(AG_SEND_VENDOR_SPECIFIC_AT_COMMAND, addr);
if (!msg)
return BT_STATUS_NOMEM;

AG_MSG_ADD_STR(msg, 1, command, strlen(command));
AG_MSG_ADD_STR(msg, 2, value, strlen(value));

return hfp_ag_send_message(msg);
}

static const hfp_ag_interface_t agInterface = {
.size = sizeof(agInterface),
.register_callbacks = hfp_ag_register_callbacks,
Expand All @@ -682,6 +697,7 @@ static const hfp_ag_interface_t agInterface = {
.volume_control = hfp_ag_volume_control,
.dial_response = hfp_ag_dial_result,
.send_at_command = hfp_ag_send_at_command,
.send_vendor_specific_at_command = hfp_ag_send_vendor_specific_at_command,
};

static const void* get_ag_profile_interface(void)
Expand Down Expand Up @@ -752,6 +768,12 @@ void ag_service_notify_cmd_received(bt_address_t* addr, const char* at_cmd)
AG_CALLBACK_FOREACH(g_ag_service.callbacks, at_cmd_cb, addr, at_cmd);
}

void ag_service_notify_vendor_specific_cmd(bt_address_t* addr, const char* command, uint16_t company_id, const char* value)
{
BT_LOGD("%s, command:%s, value:%s", __func__, command, value);
AG_CALLBACK_FOREACH(g_ag_service.callbacks, vender_specific_at_cmd_cb, addr, command, company_id, value);
}

void hfp_ag_on_connection_state_changed(bt_address_t* addr, profile_connection_state_t state,
profile_connection_reason_t reason, uint32_t remote_features)
{
Expand Down
78 changes: 70 additions & 8 deletions service/profiles/hfp_ag/hfp_ag_state_machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <sys/types.h>

#include "audio_control.h"
#include "bluetooth.h"
#include "bt_addr.h"
#include "bt_device.h"
#include "bt_hfp_ag.h"
Expand Down Expand Up @@ -65,6 +66,16 @@ typedef struct _ag_state_machine {
service_timer_t* retry_timer;
} ag_state_machine_t;

typedef struct vendor_specific_at_prefix {
char* at_prefix;
uint16_t company_id;
} vendor_specific_at_prefix_t;

static const vendor_specific_at_prefix_t company_id_map[] = {
{ "+XIAOMI", BLUETOOTH_COMPANY_ID_XIAOMI },
{ "+ANDROID", BLUETOOTH_COMPANY_ID_GOOGLE },
};

#define AG_TIMEOUT 10000
#define AG_OFFLOAD_TIMEOUT 500
#define AG_STM_DEBUG 1
Expand Down Expand Up @@ -200,6 +211,7 @@ static const char* stack_event_to_string(hfp_ag_event_t event)
CASE_RETURN_STR(AG_SET_INBAND_RING_ENABLE)
CASE_RETURN_STR(AG_DIALING_RESULT)
CASE_RETURN_STR(AG_SEND_AT_COMMAND)
CASE_RETURN_STR(AG_SEND_VENDOR_SPECIFIC_AT_COMMAND)
CASE_RETURN_STR(AG_STARTUP)
CASE_RETURN_STR(AG_SHUTDOWN)
CASE_RETURN_STR(AG_CONNECT_TIMEOUT)
Expand Down Expand Up @@ -427,6 +439,56 @@ static void ag_retry_callback(service_timer_t* timer, void* data)
agsm->retry_timer = NULL;
}

static void process_vendor_specific_at(bt_address_t* addr, const char* at_string)
{
uint8_t company_id_index;
uint16_t company_id;
size_t prefix_size;
char command[HFP_COMPANY_PREFIX_LEN_MAX + 1] = { 0 };
const char* value;
const vendor_specific_at_prefix_t* prefix;

if (!at_string)
return;

for (company_id_index = 0; company_id_index < ARRAY_SIZE(company_id_map); company_id_index++) {
prefix = &company_id_map[company_id_index];
prefix_size = strlen(prefix->at_prefix);
if (strlen(at_string) <= prefix_size + 3 /* "AT" and "=" */) {
continue;
}
if (strncmp(at_string + 2 /* "AT" */, prefix->at_prefix, prefix_size)) {
continue;
}
value = at_string + strlen(prefix->at_prefix) + 3; /* The value is the string after "AT+XIAOMI=" */
if (value[0] == '\r' || value[0] == '\n') {
break;
}
strlcpy(command, prefix->at_prefix, sizeof(command));
company_id = prefix->company_id;

BT_LOGD("%s, command:%s, company_id:0x%04X, value:%s", __FUNCTION__, command, company_id, value);
ag_service_notify_vendor_specific_cmd(addr, command, company_id, value);
bt_sal_hfp_ag_send_at_cmd(addr, "\r\nOK\r\n", sizeof("\r\nOK\r\n"));
return;
}
BT_LOGD("unknown AT command:%s", at_string);
bt_sal_hfp_ag_error_response(addr, HFP_ATCMD_RESULT_CMEERR_OPERATION_NOTSUPPORTED);
}

static void hfp_ag_send_vendor_specific_at_cmd(bt_address_t* addr, const char* command, const char* value) {
char at_command[HFP_AT_LEN_MAX+1] = "\r\n";
if (!command || !value)
return;

strlcat(at_command, command, sizeof(at_command));
strlcat(at_command, ": ", sizeof(at_command));
strlcat(at_command, value, sizeof(at_command));
strlcat(at_command, "\r\n", sizeof(at_command));
BT_LOGD("%s, command:%s, value:%s", __FUNCTION__, command, value);
bt_sal_hfp_ag_send_at_cmd(addr, at_command, strlen(at_command));
}

static bool connecting_process_event(state_machine_t* sm, uint32_t event, void* p_data)
{
ag_state_machine_t* agsm = (ag_state_machine_t*)sm;
Expand All @@ -446,6 +508,9 @@ static bool connecting_process_event(state_machine_t* sm, uint32_t event, void*
case AG_SEND_AT_COMMAND:
bt_sal_hfp_ag_send_at_cmd(&agsm->addr, data->string1, strlen(data->string1));
break;
case AG_SEND_VENDOR_SPECIFIC_AT_COMMAND:
hfp_ag_send_vendor_specific_at_cmd(&agsm->addr, data->string1, data->string2);
break;
case AG_STACK_EVENT_CONNECTION_STATE_CHANGED: {
profile_connection_state_t state = data->valueint1;
profile_connection_reason_t reason = data->valueint2;
Expand Down Expand Up @@ -482,11 +547,7 @@ static bool connecting_process_event(state_machine_t* sm, uint32_t event, void*
process_cind_request(agsm);
break;
case AG_STACK_EVENT_AT_COMMAND:
if (hfp_ag_get_local_features() & HFP_FEAT_AG_UNKNOWN_AT_CMD) {
ag_service_notify_cmd_received(&agsm->addr, data->string1);
} else {
bt_sal_hfp_ag_error_response(&agsm->addr, HFP_ATCMD_RESULT_CMEERR_OPERATION_NOTSUPPORTED);
}
process_vendor_specific_at(&agsm->addr, data->string1);
break;
case AG_OFFLOAD_START_REQ:
audio_ctrl_send_control_event(PROFILE_HFP_AG, AUDIO_CTRL_EVT_START_FAIL);
Expand Down Expand Up @@ -617,6 +678,9 @@ static bool default_process_event(state_machine_t* sm, uint32_t event, void* p_d
case AG_SEND_AT_COMMAND:
bt_sal_hfp_ag_send_at_cmd(&agsm->addr, data->string1, strlen(data->string1));
break;
case AG_SEND_VENDOR_SPECIFIC_AT_COMMAND:
hfp_ag_send_vendor_specific_at_cmd(&agsm->addr, data->string1, data->string2);
break;
case AG_DIALING_RESULT:
if (agsm->dial_out_timer) {
service_loop_cancel_timer(agsm->dial_out_timer);
Expand Down Expand Up @@ -706,10 +770,8 @@ static bool default_process_event(state_machine_t* sm, uint32_t event, void* p_d

if (at_cmd_check_test(&agsm->addr, at_cmd)) {
break;
} else if (hfp_ag_get_local_features() & HFP_FEAT_AG_UNKNOWN_AT_CMD) {
ag_service_notify_cmd_received(&agsm->addr, at_cmd);
} else {
bt_sal_hfp_ag_error_response(&agsm->addr, HFP_ATCMD_RESULT_CMEERR_OPERATION_NOTSUPPORTED);
process_vendor_specific_at(&agsm->addr, at_cmd);
}
} break;
case AG_STACK_EVENT_SEND_DTMF:
Expand Down
Loading
Loading