diff options
Diffstat (limited to 'audio/headset.c')
-rw-r--r-- | audio/headset.c | 143 |
1 files changed, 131 insertions, 12 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; } |