blob: d0e7a662be6b95f16a794a0005bbffac070ffaae [file] [log] [blame]
/*
* Copyright (C) 2013 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "if-main.h"
#include "../hal-utils.h"
const bthf_interface_t *if_hf = NULL;
SINTMAP(bthf_at_response_t, -1, "(unknown)")
DELEMENT(BTHF_AT_RESPONSE_ERROR),
DELEMENT(BTHF_AT_RESPONSE_OK),
ENDMAP
SINTMAP(bthf_connection_state_t, -1, "(unknown)")
DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTED),
DELEMENT(BTHF_CONNECTION_STATE_CONNECTING),
DELEMENT(BTHF_CONNECTION_STATE_CONNECTED),
DELEMENT(BTHF_CONNECTION_STATE_SLC_CONNECTED),
DELEMENT(BTHF_CONNECTION_STATE_DISCONNECTING),
ENDMAP
SINTMAP(bthf_audio_state_t, -1, "(unknown)")
DELEMENT(BTHF_AUDIO_STATE_DISCONNECTED),
DELEMENT(BTHF_AUDIO_STATE_CONNECTING),
DELEMENT(BTHF_AUDIO_STATE_CONNECTED),
DELEMENT(BTHF_AUDIO_STATE_DISCONNECTING),
ENDMAP
SINTMAP(bthf_vr_state_t, -1, "(unknown)")
DELEMENT(BTHF_VR_STATE_STOPPED),
DELEMENT(BTHF_VR_STATE_STARTED),
ENDMAP
SINTMAP(bthf_volume_type_t, -1, "(unknown)")
DELEMENT(BTHF_VOLUME_TYPE_SPK),
DELEMENT(BTHF_VOLUME_TYPE_MIC),
ENDMAP
SINTMAP(bthf_nrec_t, -1, "(unknown)")
DELEMENT(BTHF_NREC_STOP),
DELEMENT(BTHF_NREC_START),
ENDMAP
SINTMAP(bthf_chld_type_t, -1, "(unknown)")
DELEMENT(BTHF_CHLD_TYPE_RELEASEHELD),
DELEMENT(BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD),
DELEMENT(BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD),
DELEMENT(BTHF_CHLD_TYPE_ADDHELDTOCONF),
ENDMAP
/* Network Status */
SINTMAP(bthf_network_state_t, -1, "(unknown)")
DELEMENT(BTHF_NETWORK_STATE_NOT_AVAILABLE),
DELEMENT(BTHF_NETWORK_STATE_AVAILABLE),
ENDMAP
/* Service type */
SINTMAP(bthf_service_type_t, -1, "(unknown)")
DELEMENT(BTHF_SERVICE_TYPE_HOME),
DELEMENT(BTHF_SERVICE_TYPE_ROAMING),
ENDMAP
SINTMAP(bthf_call_state_t, -1, "(unknown)")
DELEMENT(BTHF_CALL_STATE_ACTIVE),
DELEMENT(BTHF_CALL_STATE_HELD),
DELEMENT(BTHF_CALL_STATE_DIALING),
DELEMENT(BTHF_CALL_STATE_ALERTING),
DELEMENT(BTHF_CALL_STATE_INCOMING),
DELEMENT(BTHF_CALL_STATE_WAITING),
DELEMENT(BTHF_CALL_STATE_IDLE),
ENDMAP
SINTMAP(bthf_call_direction_t, -1, "(unknown)")
DELEMENT(BTHF_CALL_DIRECTION_OUTGOING),
DELEMENT(BTHF_CALL_DIRECTION_INCOMING),
ENDMAP
SINTMAP(bthf_call_mode_t, -1, "(unknown)")
DELEMENT(BTHF_CALL_TYPE_VOICE),
DELEMENT(BTHF_CALL_TYPE_DATA),
DELEMENT(BTHF_CALL_TYPE_FAX),
ENDMAP
SINTMAP(bthf_call_mpty_type_t, -1, "(unknown)")
DELEMENT(BTHF_CALL_MPTY_TYPE_SINGLE),
DELEMENT(BTHF_CALL_MPTY_TYPE_MULTI),
ENDMAP
SINTMAP(bthf_call_addrtype_t, -1, "(unknown)")
DELEMENT(BTHF_CALL_ADDRTYPE_UNKNOWN),
DELEMENT(BTHF_CALL_ADDRTYPE_INTERNATIONAL),
ENDMAP
/* Callbacks */
static char last_addr[MAX_ADDR_STR_LEN];
/*
* Callback for connection state change.
* state will have one of the values from BtHfConnectionState
*/
static void connection_state_cb(bthf_connection_state_t state,
bt_bdaddr_t *bd_addr)
{
haltest_info("%s: state=%s bd_addr=%s\n", __func__,
bthf_connection_state_t2str(state),
bt_bdaddr_t2str(bd_addr, last_addr));
}
/*
* Callback for audio connection state change.
* state will have one of the values from BtHfAudioState
*/
static void audio_state_cb(bthf_audio_state_t state, bt_bdaddr_t *bd_addr)
{
haltest_info("%s: state=%s bd_addr=%s\n", __func__,
bthf_audio_state_t2str(state),
bt_bdaddr_t2str(bd_addr, last_addr));
}
/*
* Callback for VR connection state change.
* state will have one of the values from BtHfVRState
*/
static void vr_cmd_cb(bthf_vr_state_t state)
{
haltest_info("%s: state=%s\n", __func__, bthf_vr_state_t2str(state));
}
/* Callback for answer incoming call (ATA) */
static void answer_call_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/* Callback for disconnect call (AT+CHUP) */
static void hangup_call_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/*
* Callback for disconnect call (AT+CHUP)
* type will denote Speaker/Mic gain (BtHfVolumeControl).
*/
static void volume_cmd_cb(bthf_volume_type_t type, int volume)
{
haltest_info("%s: type=%s volume=%d\n", __func__,
bthf_volume_type_t2str(type), volume);
}
/*
* Callback for dialing an outgoing call
* If number is NULL, redial
*/
static void dial_call_cmd_cb(char *number)
{
haltest_info("%s: number=%s\n", __func__, number);
}
/*
* Callback for sending DTMF tones
* tone contains the dtmf character to be sent
*/
static void dtmf_cmd_cb(char tone)
{
haltest_info("%s: tone=%d\n", __func__, tone);
}
/*
* Callback for enabling/disabling noise reduction/echo cancellation
* value will be 1 to enable, 0 to disable
*/
static void nrec_cmd_cb(bthf_nrec_t nrec)
{
haltest_info("%s: nrec=%s\n", __func__, bthf_nrec_t2str(nrec));
}
/*
* Callback for call hold handling (AT+CHLD)
* value will contain the call hold command (0, 1, 2, 3)
*/
static void chld_cmd_cb(bthf_chld_type_t chld)
{
haltest_info("%s: chld=%s\n", __func__, bthf_chld_type_t2str(chld));
}
/* Callback for CNUM (subscriber number) */
static void cnum_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/* Callback for indicators (CIND) */
static void cind_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/* Callback for operator selection (COPS) */
static void cops_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/* Callback for call list (AT+CLCC) */
static void clcc_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
/*
* Callback for unknown AT command recd from HF
* at_string will contain the unparsed AT string
*/
static void unknown_at_cmd_cb(char *at_string)
{
haltest_info("%s: at_string=%s\n", __func__, at_string);
}
/* Callback for keypressed (HSP) event. */
static void key_pressed_cmd_cb(void)
{
haltest_info("%s\n", __func__);
}
static bthf_callbacks_t hf_cbacks = {
.size = sizeof(hf_cbacks),
.connection_state_cb = connection_state_cb,
.audio_state_cb = audio_state_cb,
.vr_cmd_cb = vr_cmd_cb,
.answer_call_cmd_cb = answer_call_cmd_cb,
.hangup_call_cmd_cb = hangup_call_cmd_cb,
.volume_cmd_cb = volume_cmd_cb,
.dial_call_cmd_cb = dial_call_cmd_cb,
.dtmf_cmd_cb = dtmf_cmd_cb,
.nrec_cmd_cb = nrec_cmd_cb,
.chld_cmd_cb = chld_cmd_cb,
.cnum_cmd_cb = cnum_cmd_cb,
.cind_cmd_cb = cind_cmd_cb,
.cops_cmd_cb = cops_cmd_cb,
.clcc_cmd_cb = clcc_cmd_cb,
.unknown_at_cmd_cb = unknown_at_cmd_cb,
.key_pressed_cmd_cb = key_pressed_cmd_cb,
};
/* init */
static void init_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
EXEC(if_hf->init, &hf_cbacks);
}
/* connect */
static void connect_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 3) {
*user = NULL;
*enum_func = enum_devices;
}
}
static void connect_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_hf);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_hf->connect, &addr);
}
/* disconnect */
/*
* This completion function will be used for several methods
* returning recently connected address
*/
static void connected_addr_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 3) {
*user = last_addr;
*enum_func = enum_one_string;
}
}
/* Map completion to connected_addr_c */
#define disconnect_c connected_addr_c
static void disconnect_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_hf);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_hf->disconnect, &addr);
}
/* create an audio connection */
/* Map completion to connected_addr_c */
#define connect_audio_c connected_addr_c
static void connect_audio_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_hf);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_hf->connect_audio, &addr);
}
/* close the audio connection */
/* Map completion to connected_addr_c */
#define disconnect_audio_c connected_addr_c
static void disconnect_audio_p(int argc, const char **argv)
{
bt_bdaddr_t addr;
RETURN_IF_NULL(if_hf);
VERIFY_ADDR_ARG(2, &addr);
EXEC(if_hf->disconnect_audio, &addr);
}
/* start voice recognition */
static void start_voice_recognition_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
EXEC(if_hf->start_voice_recognition);
}
/* stop voice recognition */
static void stop_voice_recognition_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
EXEC(if_hf->stop_voice_recognition);
}
/* volume control */
static void volume_control_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(bthf_volume_type_t);
*enum_func = enum_defines;
}
}
static void volume_control_p(int argc, const char **argv)
{
bthf_volume_type_t type;
int volume;
RETURN_IF_NULL(if_hf);
/* volume type */
if (argc <= 2) {
haltest_error("No volume type specified\n");
return;
}
type = str2bthf_volume_type_t(argv[2]);
/* volume */
if (argc <= 3) {
haltest_error("No volume specified\n");
return;
}
volume = atoi(argv[3]);
EXEC(if_hf->volume_control, type, volume);
}
/* Combined device status change notification */
static void device_status_notification_c(int argc, const char **argv,
enum_func *enum_func,
void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(bthf_network_state_t);
*enum_func = enum_defines;
} else if (argc == 4) {
*user = TYPE_ENUM(bthf_service_type_t);
*enum_func = enum_defines;
}
}
static void device_status_notification_p(int argc, const char **argv)
{
bthf_network_state_t ntk_state;
bthf_service_type_t svc_type;
int signal;
int batt_chg;
RETURN_IF_NULL(if_hf);
/* network state */
if (argc <= 2) {
haltest_error("No network state specified\n");
return;
}
ntk_state = str2bthf_network_state_t(argv[2]);
/* service type */
if (argc <= 3) {
haltest_error("No service type specified\n");
return;
}
svc_type = str2bthf_service_type_t(argv[3]);
/* signal */
if (argc <= 4) {
haltest_error("No signal specified\n");
return;
}
signal = atoi(argv[4]);
/* batt_chg */
if (argc <= 5) {
haltest_error("No batt_chg specified\n");
return;
}
batt_chg = atoi(argv[5]);
EXEC(if_hf->device_status_notification, ntk_state, svc_type, signal,
batt_chg);
}
/* Response for COPS command */
static void cops_response_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
/* response */
if (argc <= 2) {
haltest_error("No cops specified\n");
return;
}
EXEC(if_hf->cops_response, argv[2]);
}
/* Response for CIND command */
static void cind_response_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 6) {
*user = TYPE_ENUM(bthf_call_state_t);
*enum_func = enum_defines;
}
}
static void cind_response_p(int argc, const char **argv)
{
int svc;
int num_active;
int num_held;
bthf_call_state_t call_setup_state;
int signal;
int roam;
int batt_chg;
RETURN_IF_NULL(if_hf);
/* svc */
if (argc <= 2) {
haltest_error("No service specified\n");
return;
}
svc = atoi(argv[2]);
/* num active */
if (argc <= 3) {
haltest_error("No num active specified\n");
return;
}
num_active = atoi(argv[3]);
/* num held */
if (argc <= 4) {
haltest_error("No num held specified\n");
return;
}
num_held = atoi(argv[4]);
/* call setup state */
if (argc <= 5) {
haltest_error("No call setup state specified\n");
return;
}
call_setup_state = str2bthf_call_state_t(argv[5]);
/* signal */
if (argc <= 6) {
haltest_error("No signal specified\n");
return;
}
signal = atoi(argv[6]);
/* roam */
if (argc <= 7) {
haltest_error("No roam specified\n");
return;
}
roam = atoi(argv[7]);
/* batt_chg */
if (argc <= 8) {
haltest_error("No batt_chg specified\n");
return;
}
batt_chg = atoi(argv[8]);
EXEC(if_hf->cind_response, svc, num_active, num_held, call_setup_state,
signal, roam, batt_chg);
}
/* Pre-formatted AT response, typically in response to unknown AT cmd */
static void formatted_at_response_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
/* response */
if (argc <= 2) {
haltest_error("No response specified\n");
return;
}
EXEC(if_hf->formatted_at_response, argv[2]);
}
/* at_response */
static void at_response_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 3) {
*user = TYPE_ENUM(bthf_at_response_t);
*enum_func = enum_defines;
}
}
static void at_response_p(int argc, const char **argv)
{
bthf_at_response_t response_code;
int error_code = 0;
RETURN_IF_NULL(if_hf);
/* response type */
if (argc <= 2) {
haltest_error("No response specified\n");
return;
}
response_code = str2bthf_at_response_t(argv[2]);
/* error code */
if (argc >= 3)
error_code = atoi(argv[3]);
EXEC(if_hf->at_response, response_code, error_code);
}
/* response for CLCC command */
static void clcc_response_c(int argc, const char **argv, enum_func *enum_func,
void **user)
{
if (argc == 4) {
*user = TYPE_ENUM(bthf_call_direction_t);
*enum_func = enum_defines;
} else if (argc == 5) {
*user = TYPE_ENUM(bthf_call_state_t);
*enum_func = enum_defines;
} else if (argc == 6) {
*user = TYPE_ENUM(bthf_call_mode_t);
*enum_func = enum_defines;
} else if (argc == 7) {
*user = TYPE_ENUM(bthf_call_mpty_type_t);
*enum_func = enum_defines;
} else if (argc == 9) {
*user = TYPE_ENUM(bthf_call_addrtype_t);
*enum_func = enum_defines;
}
}
static void clcc_response_p(int argc, const char **argv)
{
int index;
bthf_call_direction_t dir;
bthf_call_state_t state;
bthf_call_mode_t mode;
bthf_call_mpty_type_t mpty;
const char *number;
bthf_call_addrtype_t type;
RETURN_IF_NULL(if_hf);
/* index */
if (argc <= 2) {
haltest_error("No index specified\n");
return;
}
index = atoi(argv[2]);
/* direction */
if (argc <= 3) {
haltest_error("No direction specified\n");
return;
}
dir = str2bthf_call_direction_t(argv[3]);
/* call state */
if (argc <= 4) {
haltest_error("No call state specified\n");
return;
}
state = str2bthf_call_state_t(argv[4]);
/* call mode */
if (argc <= 5) {
haltest_error("No mode specified\n");
return;
}
mode = str2bthf_call_mode_t(argv[5]);
/* call mpty type */
if (argc <= 6) {
haltest_error("No mpty type specified\n");
return;
}
mpty = str2bthf_call_mpty_type_t(argv[6]);
/* number */
if (argc <= 7) {
haltest_error("No number specified\n");
return;
}
number = argv[7];
/* call mpty type */
if (argc <= 8) {
haltest_error("No address type specified\n");
return;
}
type = str2bthf_call_addrtype_t(argv[8]);
EXEC(if_hf->clcc_response, index, dir, state, mode, mpty, number,
type);
}
/* phone state change */
static void phone_state_change_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
if (argc == 5) {
*user = TYPE_ENUM(bthf_call_state_t);
*enum_func = enum_defines;
} else if (argc == 7) {
*user = TYPE_ENUM(bthf_call_addrtype_t);
*enum_func = enum_defines;
}
}
static void phone_state_change_p(int argc, const char **argv)
{
int num_active;
int num_held;
bthf_call_state_t call_setup_state;
const char *number;
bthf_call_addrtype_t type;
RETURN_IF_NULL(if_hf);
/* num_active */
if (argc <= 2) {
haltest_error("No num_active specified\n");
return;
}
num_active = atoi(argv[2]);
/* num_held */
if (argc <= 3) {
haltest_error("No num_held specified\n");
return;
}
num_held = atoi(argv[3]);
/* setup state */
if (argc <= 4) {
haltest_error("No call setup state specified\n");
return;
}
call_setup_state = str2bthf_call_state_t(argv[4]);
/* number */
if (argc <= 5) {
haltest_error("No number specified\n");
return;
}
number = argv[5];
/* call mpty type */
if (argc <= 6) {
haltest_error("No address type specified\n");
return;
}
type = str2bthf_call_addrtype_t(argv[6]);
EXEC(if_hf->phone_state_change, num_active, num_held, call_setup_state,
number, type);
}
/* cleanup */
static void cleanup_p(int argc, const char **argv)
{
RETURN_IF_NULL(if_hf);
EXECV(if_hf->cleanup);
if_hf = NULL;
}
static struct method methods[] = {
STD_METHOD(init),
STD_METHODCH(connect, "<addr>"),
STD_METHODCH(disconnect, "<addr>"),
STD_METHODCH(connect_audio, "<addr>"),
STD_METHODCH(disconnect_audio, "<addr>"),
STD_METHOD(start_voice_recognition),
STD_METHOD(stop_voice_recognition),
STD_METHODCH(volume_control, "<vol_type> <volume>"),
STD_METHODCH(device_status_notification,
"<ntk_state> <svt_type> <signal> <batt_chg>"),
STD_METHODH(cops_response, "<cops string>"),
STD_METHODCH(cind_response,
"<svc> <num_active> <num_held> <setup_state> <signal> <roam> <batt_chg>"),
STD_METHODH(formatted_at_response, "<at_response>"),
STD_METHODCH(at_response, "<response_code> [<error_code>]"),
STD_METHODCH(clcc_response,
"<index> <direction> <state> <mode> <mpty> <number> <type>"),
STD_METHODCH(phone_state_change,
"<num_active> <num_held> <setup_state> <number> <type>"),
STD_METHOD(cleanup),
END_METHOD
};
const struct interface hf_if = {
.name = "handsfree",
.methods = methods
};