diff options
-rw-r--r-- | audio/headset.c | 143 | ||||
-rw-r--r-- | audio/manager.c | 1 | ||||
-rw-r--r-- | audio/telephony-dummy.c | 35 | ||||
-rw-r--r-- | audio/telephony.h | 11 |
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); |