@@ -54,6 +54,7 @@ enum qmi_ussd_user_required {
/* QMI service voice. Using an enum to prevent doublicated entries */
enum voice_commands {
QMI_VOICE_DIAL_CALL = 0x20,
+ QMI_VOICE_END_CALL = 0x21,
QMI_VOICE_ANSWER_CALL = 0x22,
QMI_VOICE_SUPS_NOTIFICATION_IND = 0x32,
QMI_VOICE_SET_SUPS_SERVICE = 0x33,
@@ -119,6 +120,14 @@ enum qmi_voice_call_answer_return {
QMI_VOICE_ANSWER_RETURN_CALL_ID = 0x10,
};
+enum qmi_voice_call_end_param {
+ QMI_VOICE_END_CALL_ID = 0x01,
+};
+
+enum qmi_voice_call_end_return {
+ QMI_VOICE_END_RETURN_CALL_ID = 0x10,
+};
+
enum parse_error {
NONE = 0,
MISSING_MANDATORY = 1,
@@ -107,6 +107,16 @@ struct qmi_voice_answer_call_result {
uint8_t call_id;
};
+struct qmi_voice_end_call_arg {
+ bool call_id_set;
+ uint8_t call_id;
+};
+
+struct qmi_voice_end_call_result {
+ bool call_id_set;
+ uint8_t call_id;
+};
+
int ofono_call_compare(const void *a, const void *b, void *data)
{
const struct ofono_call *ca = a;
@@ -631,6 +641,107 @@ error:
l_free(param);
}
+enum parse_error
+qmi_voice_end_call_parse(struct qmi_result *qmi_result,
+ struct qmi_voice_end_call_result *result)
+{
+ int err = NONE;
+
+ /* optional */
+ if (qmi_result_get_uint8(qmi_result, QMI_VOICE_END_RETURN_CALL_ID, &result->call_id))
+ result->call_id_set = 1;
+
+ return err;
+}
+
+static void end_call_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_voicecall_cb_t cb = cbd->cb;
+ uint16_t error;
+ struct qmi_voice_end_call_result end_result;
+
+ if (qmi_result_set_error(result, &error)) {
+ DBG("QMI Error %d", error);
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ return;
+ }
+
+ if (qmi_voice_end_call_parse(result, &end_result) != NONE) {
+ DBG("Received invalid Result");
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ return;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void release_specific(struct ofono_voicecall *vc, int id,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ struct qmi_voice_end_call_arg arg;
+ struct qmi_param *param = NULL;
+
+ DBG("");
+ cbd->user = vc;
+
+ arg.call_id_set = true;
+ arg.call_id = id;
+
+ param = qmi_param_new();
+ if (!param)
+ goto error;
+
+ if (arg.call_id_set) {
+ if (!qmi_param_append_uint8(param, QMI_VOICE_END_CALL_ID, arg.call_id)) {
+ goto error;
+ }
+ }
+
+ if (qmi_service_send(vd->voice, QMI_VOICE_END_CALL, param, end_call_cb, cbd, l_free) >
+ 0) {
+ return;
+ }
+
+error:
+ CALLBACK_WITH_FAILURE(cb, data);
+ l_free(cbd);
+ l_free(param);
+}
+
+static void hangup_active(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
+ void *data)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+ struct ofono_call *call;
+ enum call_status active[] = {
+ CALL_STATUS_ACTIVE,
+ CALL_STATUS_DIALING,
+ CALL_STATUS_ALERTING,
+ CALL_STATUS_INCOMING,
+ };
+ int i;
+
+ DBG("");
+ for (i = 0; i < L_ARRAY_SIZE(active); i++) {
+ call = l_queue_find(vd->call_list, ofono_call_compare_by_status,
+ L_INT_TO_PTR(active[i]));
+
+ if (call)
+ break;
+ }
+
+ if (call == NULL) {
+ DBG("Can not find a call to hang up");
+ CALLBACK_WITH_FAILURE(cb, data);
+ return;
+ }
+
+ release_specific(vc, call->id, cb, data);
+}
+
static void create_voice_cb(struct qmi_service *service, void *user_data)
{
struct ofono_voicecall *vc = user_data;
@@ -697,6 +808,8 @@ static const struct ofono_voicecall_driver driver = {
.remove = qmi_voicecall_remove,
.dial = dial,
.answer = answer,
+ .hangup_active = hangup_active,
+ .release_specific = release_specific,
};
OFONO_ATOM_DRIVER_BUILTIN(voicecall, qmimodem, &driver)