summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/headset.c143
-rw-r--r--audio/manager.c1
-rw-r--r--audio/telephony-dummy.c35
-rw-r--r--audio/telephony.h11
4 files changed, 164 insertions, 26 deletions
diff --git a/audio/headset.c b/audio/headset.c
index bf4d64cd..c4eefe23 100644
--- a/audio/headset.c
+++ b/audio/headset.c
@@ -67,10 +67,25 @@
#define HEADSET_GAIN_SPEAKER 'S'
#define HEADSET_GAIN_MICROPHONE 'M'
-static uint32_t ag_features = 0;
+static struct {
+ gboolean telephony_ready; /* Telephony plugin initialized */
+ uint32_t features; /* HFP AG features */
+ struct indicator *indicators; /* Available HFP indicators */
+ int er_mode; /* Event reporting mode */
+ int er_ind; /* Event reporting for indicators */
+ int rh; /* Response and Hold state */
+} ag = {
+ .telephony_ready = FALSE,
+ .features = 0,
+ .er_mode = 3,
+ .er_ind = 0,
+ .rh = -1,
+};
static gboolean sco_hci = TRUE;
+static struct audio_device *active_telephony_device = NULL;
+
static char *str_state[] = {
"HEADSET_STATE_DISCONNECTED",
"HEADSET_STATE_CONNECT_IN_PROGRESS",
@@ -198,7 +213,7 @@ static int supported_features(struct audio_device *device, const char *buf)
return -EINVAL;
hs->hfp_features = strtoul(&buf[8], NULL, 10);
- err = headset_send(hs, "\r\n+BRSF=%u\r\n", ag_features);
+ err = headset_send(hs, "\r\n+BRSF=%u\r\n", ag.features);
if (err < 0)
return err;
@@ -252,16 +267,14 @@ static int report_indicators(struct audio_device *device, const char *buf)
struct headset *hs = device->headset;
int err;
char *str;
- struct indicator *indicators;
- indicators = telephony_indicators_req();
- if (!indicators)
- return headset_send(hs, "\r\nERROR\r\n");
+ if (strlen(buf) < 8)
+ return -EINVAL;
if (buf[7] == '=')
- str = indicator_ranges(indicators);
+ str = indicator_ranges(ag.indicators);
else
- str = indicator_values(indicators);
+ str = indicator_values(ag.indicators);
err = headset_send(hs, str);
@@ -450,6 +463,34 @@ static int event_reporting(struct audio_device *dev, const char *buf)
{
struct headset *hs = dev->headset;
int ret;
+ char **tokens; /* <mode>, <keyp>, <disp>, <ind>, <bfr> */
+
+ if (strlen(buf) < 13)
+ return -EINVAL;
+
+ tokens = g_strsplit(&buf[8], ",", 5);
+ if (g_strv_length(tokens) < 4) {
+ g_strfreev(tokens);
+ return -EINVAL;
+ }
+
+ ag.er_mode = atoi(tokens[0]);
+ ag.er_ind = atoi(tokens[3]);
+
+ g_strfreev(tokens);
+ tokens = NULL;
+
+ debug("Event reporting (CMER): mode=%d, ind=%d",
+ ag.er_mode, ag.er_ind);
+
+ switch (ag.er_ind) {
+ case 0:
+ case 1:
+ telephony_event_reporting_req(ag.er_ind);
+ break;
+ default:
+ return -EINVAL;
+ }
ret = headset_send(hs, "\r\nOK\r\n");
if (ret < 0)
@@ -458,7 +499,7 @@ static int event_reporting(struct audio_device *dev, const char *buf)
if (hs->state != HEADSET_STATE_CONNECT_IN_PROGRESS)
return 0;
- if (ag_features & AG_FEATURE_THREE_WAY_CALLING)
+ if (ag.features & AG_FEATURE_THREE_WAY_CALLING)
return 0;
hfp_slc_complete(dev);
@@ -563,6 +604,24 @@ static int cli_notification(struct audio_device *device, const char *buf)
return headset_send(hs, "\r\nOK\r\n");
}
+static int response_and_hold(struct audio_device *device, const char *buf)
+{
+ struct headset *hs = device->headset;
+
+ if (strlen(buf) < 8)
+ return -EINVAL;
+
+ if (buf[7] == '=') {
+ if (telephony_response_and_hold_req(atoi(&buf[8]) < 0)) {
+ headset_send(hs, "\r\nERROR\r\n");
+ return 0;
+ }
+ } else
+ headset_send(hs, "\r\n+BTRH:%d\r\n", ag.rh);
+
+ return headset_send(hs, "\r\nOK\n\r", ag.rh);
+}
+
static int signal_gain_setting(struct audio_device *device, const char *buf)
{
struct headset *hs = device->headset;
@@ -618,6 +677,7 @@ static struct event event_callbacks[] = {
{ "AT+CHUP", terminate_call },
{ "AT+CKPD", answer_call },
{ "AT+CLIP", cli_notification },
+ { "AT+BTRH", response_and_hold },
{ 0 }
};
@@ -1608,7 +1668,7 @@ uint32_t headset_config_init(GKeyFile *config)
/* Use the default values if there is no config file */
if (config == NULL)
- return ag_features;
+ return ag.features;
str = g_key_file_get_string(config, "General", "SCORouting",
&err);
@@ -1626,7 +1686,7 @@ uint32_t headset_config_init(GKeyFile *config)
g_free(str);
}
- return ag_features;
+ return ag.features;
}
static gboolean hs_dc_timeout(struct audio_device *dev)
@@ -1795,6 +1855,9 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
AUDIO_HEADSET_INTERFACE,
"Disconnected",
DBUS_TYPE_INVALID);
+ telephony_event_reporting_req(0);
+ if (dev == active_telephony_device)
+ active_telephony_device = NULL;
break;
case HEADSET_STATE_CONNECT_IN_PROGRESS:
break;
@@ -1805,6 +1868,8 @@ void headset_set_state(struct audio_device *dev, headset_state_t state)
AUDIO_HEADSET_INTERFACE,
"Connected",
DBUS_TYPE_INVALID);
+ if (!active_telephony_device)
+ active_telephony_device = dev;
} else if (hs->state == HEADSET_STATE_PLAYING) {
g_dbus_emit_signal(dev->conn, dev->path,
AUDIO_HEADSET_INTERFACE,
@@ -1920,5 +1985,59 @@ int headset_get_sco_fd(struct audio_device *dev)
void telephony_features_rsp(uint32_t features)
{
- ag_features = features;
+ ag.features = features;
+}
+
+int telephony_event_ind(int index)
+{
+ struct headset *hs;
+
+ if (!active_telephony_device)
+ return -ENODEV;
+
+ hs = active_telephony_device->headset;
+
+ if (!hs->hfp_active)
+ return -EINVAL;
+
+ if (!ag.er_ind) {
+ debug("telephony_report_event called but events are disabled");
+ return -EINVAL;
+ }
+
+ return headset_send(hs, "\r\n+CIEV:%d,%d\r\n", index + 1,
+ ag.indicators[index].val);
+}
+
+int telephony_response_and_hold_ind(int rh)
+{
+ struct headset *hs;
+
+ if (!active_telephony_device)
+ return -ENODEV;
+
+ hs = active_telephony_device->headset;
+
+ if (!hs->hfp_active)
+ return -EINVAL;
+
+ ag.rh = rh;
+
+ /* If we aren't in any response and hold state don't send anything */
+ if (ag.rh < 0)
+ return 0;
+
+ return headset_send(hs, "\r\n+BTRH:%d\r\n", ag.rh);
+}
+
+int telephony_ready(uint32_t features, struct indicator *indicators, int rh)
+{
+ ag.telephony_ready = TRUE;
+ ag.features = features;
+ ag.indicators = indicators;
+ ag.rh = rh;
+
+ debug("Telephony plugin initialized");
+
+ return 0;
}
diff --git a/audio/manager.c b/audio/manager.c
index 4f31bb46..7df5c292 100644
--- a/audio/manager.c
+++ b/audio/manager.c
@@ -935,7 +935,6 @@ int audio_manager_init(DBusConnection *conn, GKeyFile *conf)
proceed:
if (enabled.headset) {
telephony_init();
- telephony_features_req();
btd_register_adapter_driver(&headset_server_driver);
}
diff --git a/audio/telephony-dummy.c b/audio/telephony-dummy.c
index 85bd0d12..6c003066 100644
--- a/audio/telephony-dummy.c
+++ b/audio/telephony-dummy.c
@@ -28,40 +28,55 @@
#include <stdlib.h>
#include <stdio.h>
+#include <stdint.h>
+#include <glib.h>
#include "telephony.h"
+static gboolean events_enabled = FALSE;
+
+/* Response and hold state
+ * -1 = none
+ * 0 = incoming call is put on hold in the AG
+ * 1 = held incoming call is accepted in the AG
+ * 2 = held incoming call is rejected in the AG
+ */
+static int response_and_hold = -1;
+
static struct indicator indicators[] =
{
{ "battchg", "0-5", 5 },
{ "signal", "0-5", 5 },
{ "service", "0,1", 1 },
- { "sounder", "0,1", 0 },
- { "message", "0,1", 0 },
{ "call", "0,1", 0 },
{ "callsetup", "0-3", 0 },
- { "vox", "0,1", 0 },
+ { "callheld", "0-2", 0 },
{ "roam", "0,1", 0 },
- { "smsfull", "0,1", 0 },
{ NULL }
};
-int telephony_features_req(void)
+int telephony_event_reporting_req(int ind)
{
- uint32_t features = 0;
-
- telephony_features_rsp(features);
+ events_enabled = ind == 1 ? TRUE : FALSE;
return 0;
}
-struct indicator *telephony_indicators_req(void)
+int telephony_response_and_hold_req(int rh)
{
- return indicators;
+ response_and_hold = rh;
+
+ telephony_response_and_hold_ind(response_and_hold);
+
+ return 0;
}
int telephony_init(void)
{
+ uint32_t features = 0;
+
+ telephony_ready(features, indicators, response_and_hold);
+
return 0;
}
diff --git a/audio/telephony.h b/audio/telephony.h
index 8b8ebf12..4cc74ede 100644
--- a/audio/telephony.h
+++ b/audio/telephony.h
@@ -40,10 +40,15 @@ struct indicator {
int val;
};
-int telephony_features_req(void);
-void telephony_features_rsp(uint32_t features);
+int telephony_event_reporting_req(int ind);
-struct indicator *telephony_indicators_req(void);
+int telephony_event_ind(int index);
+
+int telephony_response_and_hold_req(int rh);
+
+int telephony_response_and_hold_ind(int rh);
+
+int telephony_ready(uint32_t features, struct indicator *indicators, int rh);
int telephony_init(void);
void telephony_exit(void);