isimodem: Adapt and refactor voicecall driver
authorAki Niemi <aki.niemi@nokia.com>
Sun, 14 Nov 2010 17:31:06 +0000 (19:31 +0200)
committerAki Niemi <aki.niemi@nokia.com>
Wed, 22 Dec 2010 15:13:47 +0000 (17:13 +0200)
drivers/isimodem/voicecall.c

index f2fd53a..fe4a572 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 
 #include <glib.h>
 
-#include <gisi/netlink.h>
 #include <gisi/client.h>
+#include <gisi/message.h>
 #include <gisi/iter.h>
 
 #include <ofono/log.h>
 #include "call.h"
 #include "debug.h"
 
+#define ISI_CALL_TIMEOUT       1000
+
 struct isi_call {
-       uint8_t id, call_id, status, mode, mode_info, cause_type, cause;
-       uint8_t addr_type, presentation;
+       uint8_t id;
+       uint8_t call_id;
+       uint8_t status;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t cause_type;
+       uint8_t cause;
+       uint8_t addr_type;
+       uint8_t presentation;
        uint8_t reason;
-       char address[20], addr_pad[4];
+       char address[20];
+       char addr_pad[4];
 };
 
-struct isi_voicecall {
-       GIsiClient *client;
-
-       struct isi_call_req_context *queue;
-
-       struct isi_call calls[8];
+struct call_addr_info {
+       uint8_t call_id;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t status;
+       uint8_t filler[2];
+       uint8_t addr_type;
+       uint8_t presentation;
+       uint8_t filler2;
+       uint8_t addr_len;
 };
 
-/* ------------------------------------------------------------------------- */
-
-typedef void GIsiIndication(GIsiClient *client,
-               const void *restrict data, size_t len,
-               uint16_t object, void *opaque);
+struct call_info {
+       uint8_t call_id;
+       uint8_t mode;
+       uint8_t mode_info;
+       uint8_t status;
+};
 
-typedef void GIsiVerify(GIsiClient *client, gboolean alive, uint16_t object,
-                               void *opaque);
+struct isi_voicecall {
+       GIsiClient *client;
 
-typedef gboolean GIsiResponse(GIsiClient *client,
-                               void const *restrict data, size_t len,
-                               uint16_t object, void *opaque);
+       struct isi_call_req_ctx *queue;
 
-enum {
-       ISI_CALL_TIMEOUT = 1000,
+       struct isi_call calls[8];
 };
 
-/* ------------------------------------------------------------------------- */
-/* Request context for voicecall cb */
-
-typedef void isi_call_req_step(struct isi_call_req_context *,
-                               int id, int status);
+typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason);
 
-struct isi_call_req_context {
-       struct isi_call_req_context *next;
-       struct isi_call_req_context **prev;
+struct isi_call_req_ctx {
+       struct isi_call_req_ctx *next;
+       struct isi_call_req_ctx **prev;
        isi_call_req_step *step;
-       int id;
        struct ofono_voicecall *ovc;
        ofono_voicecall_cb_t cb;
        void *data;
 };
 
-static struct isi_call_req_context *isi_call_req_new(
-                                       struct ofono_voicecall *ovc,
-                                       ofono_voicecall_cb_t cb,
-                                       void *data)
+static struct isi_call_req_ctx *isi_call_req(struct ofono_voicecall *ovc,
+                                               const void *__restrict req,
+                                               size_t len,
+                                               GIsiNotifyFunc handler,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
 {
-       struct isi_call_req_context *irc;
+       struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       struct isi_call_req_ctx *irc;
 
-       irc = g_try_new0(struct isi_call_req_context, 1);
-       if (irc == NULL)
-               goto failed;
+       irc = g_try_new0(struct isi_call_req_ctx, 1);
+       if (!irc) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return NULL;
+       }
 
        irc->ovc = ovc;
        irc->cb = cb;
        irc->data = data;
 
+       if (!g_isi_client_send(ivc->client, req, len, ISI_CALL_TIMEOUT,
+                               handler, irc, NULL)) {
+               g_free(irc);
+               return NULL;
+       }
+
        return irc;
+}
 
-failed:
-       if (cb)
-               CALLBACK_WITH_FAILURE(cb, data);
+static void isi_ctx_queue(struct isi_call_req_ctx *irc, isi_call_req_step *next)
+{
+       struct isi_voicecall *ivc;
 
-       return NULL;
+       if (irc->prev != NULL) {
+               irc->step = next;
+               return;
+       }
+
+       ivc = ofono_voicecall_get_data(irc->ovc);
+       if (ivc->queue) {
+               irc->next = ivc->queue;
+               irc->next->prev = &irc->next;
+       }
+
+       irc->prev = &ivc->queue;
+       ivc->queue = irc;
 }
 
-static struct isi_call_req_context *isi_call_req(struct ofono_voicecall *ovc,
-                                               void const *restrict req,
-                                               size_t len,
-                                               GIsiResponse *handler,
-                                               ofono_voicecall_cb_t cb,
-                                               void *data)
+static void isi_ctx_remove(struct isi_call_req_ctx *irc)
 {
-       struct isi_voicecall *ivc;
-       struct isi_call_req_context *irc;
+       if (!irc->prev)
+               return;
 
-       irc = isi_call_req_new(ovc, cb, data);
-       if (irc == NULL)
-               return NULL;
+       *irc->prev = irc->next;
 
-       ivc = ofono_voicecall_get_data(ovc);
+       if (irc->next) {
+               irc->next->prev = irc->prev;
+               irc->next = NULL;
+       }
+       irc->prev = NULL;
+}
 
-       if (g_isi_send(ivc->client, req, len,
-                       ISI_CALL_TIMEOUT, handler, irc, NULL))
-               return irc;
+static void isi_ctx_free(struct isi_call_req_ctx *irc)
+{
+       if (!irc)
+               return;
 
+       isi_ctx_remove(irc);
        g_free(irc);
+}
 
-       if (cb)
-               CALLBACK_WITH_FAILURE(cb, data);
+static gboolean isi_ctx_return(struct isi_call_req_ctx *irc,
+                               enum ofono_error_type type, int error)
+{
+       if (!irc)
+               return TRUE;
+
+       if (irc->cb) {
+               struct ofono_error e = {
+                       .type = type,
+                       .error = error
+               };
+               irc->cb(&e, irc->data);
+       }
 
-       return NULL;
+       isi_ctx_free(irc);
+       return TRUE;
 }
 
-static void isi_ctx_queue(struct isi_call_req_context *irc,
-                               isi_call_req_step *next,
-                               int id)
+static gboolean isi_ctx_return_failure(struct isi_call_req_ctx *irc)
 {
-       if (irc->prev == NULL) {
-               struct isi_voicecall *ivc = ofono_voicecall_get_data(irc->ovc);
+       return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0);
+}
 
-               if (ivc->queue) {
-                       irc->next = ivc->queue;
-                       irc->next->prev = &irc->next;
-               }
-               irc->prev = &ivc->queue;
-               ivc->queue = irc;
-       }
+static gboolean isi_ctx_return_success(struct isi_call_req_ctx *irc)
+{
+       if (!irc || !irc->step)
+               return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0);
 
-       irc->step = next;
-       irc->id = id;
+       irc->step(irc, 0);
+       return TRUE;
 }
 
-static void isi_ctx_remove(struct isi_call_req_context *irc)
+/* Decoding subblocks */
+static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc,
+                                               struct isi_call *call,
+                                               GIsiSubBlockIter *sb)
 {
-       if (irc->prev) {
-               *irc->prev = irc->next;
+       uint8_t type;
+       uint8_t pres;
+       uint8_t len;
+       char *addr;
+
+       if (!g_isi_sb_iter_get_byte(sb, &type, 2) ||
+                       !g_isi_sb_iter_get_byte(sb, &pres, 3) ||
+                       !g_isi_sb_iter_get_byte(sb, &len, 5) ||
+                       !g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * len, 6))
+               return;
 
-               if (irc->next) {
-                       irc->next->prev = irc->prev;
-                       irc->next = NULL;
-               }
-               irc->prev = NULL;
-       }
+       call->addr_type = type | 0x80;
+       call->presentation = pres;
+       strncpy(call->address, addr, sizeof(call->address));
+
+       g_free(addr);
 }
 
-static void isi_ctx_free(struct isi_call_req_context *irc)
+static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc,
+                                               struct isi_call *call,
+                                               GIsiSubBlockIter *sb)
 {
-       if (irc) {
-               isi_ctx_remove(irc);
-               g_free(irc);
-       }
+       if (call->address[0] == '\0')
+               isi_call_any_address_sb_proc(ivc, call, sb);
 }
 
-static gboolean isi_ctx_return(struct isi_call_req_context *irc,
-                               enum ofono_error_type type,
-                               int error)
+static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc,
+                                                       struct isi_call *call,
+                                                       GIsiSubBlockIter *sb)
 {
-       if (irc == NULL)
-               return TRUE;
+       if (call->address[0] == '\0')
+               isi_call_any_address_sb_proc(ivc, call, sb);
+}
 
-       if (irc->cb) {
-               struct ofono_error e = { .type = type, .error = error };
-               irc->cb(&e, irc->data);
-       }
+static void isi_call_mode_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
+{
+       uint8_t mode;
+       uint8_t info;
 
-       isi_ctx_free(irc);
+       if (!g_isi_sb_iter_get_byte(sb, &mode, 2) ||
+                       !g_isi_sb_iter_get_byte(sb, &info, 3))
+               return;
 
-       return TRUE;
+       call->mode = mode;
+       call->mode_info = info;
 }
 
-static gboolean isi_ctx_return_failure(struct isi_call_req_context *irc)
+static void isi_call_cause_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
 {
-       return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0);
+       uint8_t type;
+       uint8_t cause;
+
+       if (!g_isi_sb_iter_get_byte(sb, &type, 2) ||
+                       !g_isi_sb_iter_get_byte(sb, &cause, 3))
+               return;
+
+       call->cause_type = type;
+       call->cause = cause;
 }
 
-static gboolean isi_ctx_return_success(struct isi_call_req_context *irc)
+static void isi_call_status_sb_proc(struct isi_voicecall *ivc,
+                                       struct isi_call *call,
+                                       GIsiSubBlockIter *sb)
 {
-       if (irc && irc->step) {
-               irc->step(irc, 0, 0);
-               return TRUE;
+       uint8_t status;
+
+       if (!g_isi_sb_iter_get_byte(sb, &status, 2))
+               return;
+
+       call->status = status;
+}
+
+static struct isi_call *isi_call_status_info_sb_proc(struct isi_voicecall *ivc,
+                                                       GIsiSubBlockIter *sb)
+{
+       struct isi_call *call = NULL;
+       int i;
+       struct call_info *ci;
+       size_t len = sizeof(struct call_info);
+
+       if (!g_isi_sb_iter_get_struct(sb, (void *)&ci, len, 2))
+               return NULL;
+
+       i = ci->call_id & 7;
+
+       if (1 <= i && i <= 7) {
+               call = &ivc->calls[i];
+               call->call_id = ci->call_id;
+               call->status = ci->status;
+               call->mode = ci->mode;
+               call->mode_info = ci->mode_info;
        }
 
-       return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0);
+       return call;
 }
 
-/* ------------------------------------------------------------------------- */
-/* Notify */
-
-enum clcc_status {
-       CLCC_STATUS_EARLY               = -1,
-       CLCC_STATUS_ACTIVE              = 0,
-       CLCC_STATUS_HOLD                = 1,
-       CLCC_STATUS_DIALING             = 2,
-       CLCC_STATUS_ALERTING            = 3,
-       CLCC_STATUS_INCOMING            = 4,
-       CLCC_STATUS_WAITING             = 5,
-       CLCC_STATUS_DISCONNECTED        = 6,
-};
+static struct isi_call *isi_call_addr_and_status_info_sb_proc(
+                                               struct isi_voicecall *ivc,
+                                               GIsiSubBlockIter *sb)
+{
+       struct isi_call *call = NULL;
+       int i;
+       struct call_addr_info *ci;
+       size_t len = sizeof(struct call_addr_info);
+       char *addr;
+
+       if (!g_isi_sb_iter_get_struct(sb, (void *)&ci, len, 2))
+               return NULL;
+
+       if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * ci->addr_len, 12))
+               return NULL;
+
+       i = ci->call_id & 7;
+
+       if (1 <= i && i <= 7) {
+               call = &ivc->calls[i];
+               call->call_id = ci->call_id;
+               call->status = ci->status;
+               call->mode = ci->mode;
+               call->mode_info = ci->mode_info;
+               call->addr_type = ci->addr_type | 0x80;
+               call->presentation = ci->presentation;
+               strncpy(call->address, addr, sizeof call->address);
+       }
+
+       g_free(addr);
+       return call;
+}
 
-/** Get +CLCC status */
-static int isi_call_status_to_clcc(struct isi_voicecall const *ivc,
-                                       struct isi_call const *call)
+static int isi_call_status_to_clcc(const struct isi_call *call)
 {
        switch (call->status) {
        case CALL_STATUS_CREATE:
-               return CLCC_STATUS_DIALING;
+               return 2;
 
        case CALL_STATUS_COMING:
-               return CLCC_STATUS_EARLY;
+               return 4;
 
        case CALL_STATUS_PROCEEDING:
+
                if ((call->mode_info & CALL_MODE_ORIGINATOR))
-                       return CLCC_STATUS_EARLY; /* MT */
+                       return 4; /* MT */
                else
-                       return CLCC_STATUS_DIALING; /* MO */
+                       return 2; /* MO */
 
        case CALL_STATUS_MO_ALERTING:
-               return CLCC_STATUS_ALERTING;
+               return 3;
 
        case CALL_STATUS_MT_ALERTING:
-               return CLCC_STATUS_INCOMING;
+               return 4;
 
        case CALL_STATUS_WAITING:
-               return CLCC_STATUS_WAITING;
+               return 5;
 
        case CALL_STATUS_ANSWERED:
        case CALL_STATUS_ACTIVE:
+       case CALL_STATUS_MO_RELEASE:
+       case CALL_STATUS_MT_RELEASE:
        case CALL_STATUS_HOLD_INITIATED:
-       case CALL_STATUS_RECONNECT_PENDING:
-       case CALL_STATUS_SWAP_INITIATED:
-               return CLCC_STATUS_ACTIVE;
+               return 0;
 
        case CALL_STATUS_HOLD:
        case CALL_STATUS_RETRIEVE_INITIATED:
-               return CLCC_STATUS_HOLD;
+               return 1;
 
-       case CALL_STATUS_MO_RELEASE:
-       case CALL_STATUS_MT_RELEASE:
+       case CALL_STATUS_RECONNECT_PENDING:
        case CALL_STATUS_TERMINATED:
-       case CALL_STATUS_IDLE:
-               return CLCC_STATUS_DISCONNECTED;
+       case CALL_STATUS_SWAP_INITIATED:
+               return 0;
        }
-
-       return CLCC_STATUS_ACTIVE;
+       return 0;
 }
 
-static struct ofono_call isi_call_as_ofono_call(struct isi_voicecall const *ivc,
-                                               struct isi_call const *call)
+static struct ofono_call isi_call_as_ofono_call(const struct isi_call *call)
 {
-       struct ofono_call ocall = { call->id };
+       struct ofono_call ocall = {
+               call->id
+       };
        struct ofono_phone_number *number = &ocall.phone_number;
 
        ocall.type = 0; /* Voice call */
        ocall.direction = call->mode_info & CALL_MODE_ORIGINATOR;
-       ocall.status = isi_call_status_to_clcc(ivc, call);
+       ocall.status = isi_call_status_to_clcc(call);
+
        memcpy(number->number, call->address, sizeof(number->number));
+
        number->type = 0x80 | call->addr_type;
        ocall.clip_validity = call->presentation & 3;
 
@@ -297,249 +406,130 @@ static struct ofono_call isi_call_as_ofono_call(struct isi_voicecall const *ivc,
        return ocall;
 }
 
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
+{
+       if (g_isi_msg_error(msg) < 0) {
+               DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+               return FALSE;
+       }
+
+       if (g_isi_msg_id(msg) != msgid) {
+               DBG("Unexpected msg: %s",
+                       net_message_id_name(g_isi_msg_id(msg)));
+               return FALSE;
+       }
+       return TRUE;
+}
+
 static struct isi_call *isi_call_set_idle(struct isi_call *call)
 {
        uint8_t id;
 
+       if (!call)
+               return NULL;
+
        id = call->id;
-       memset(call, 0, sizeof(*call));
+       memset(call, 0, sizeof(struct isi_call));
        call->id = id;
 
        return call;
 }
 
-static void isi_call_disconnected(struct ofono_voicecall *ovc,
-                                       struct isi_call *call)
+static void isi_call_release(struct ofono_voicecall *ovc, struct isi_call *call)
 {
-       struct ofono_error error = { OFONO_ERROR_TYPE_NO_ERROR, 0 };
-       enum ofono_disconnect_reason reason = call->reason;
+       struct ofono_error error = {
+               OFONO_ERROR_TYPE_NO_ERROR, 0
+       };
+       enum ofono_disconnect_reason reason;
 
-       if (!reason)
+       switch (call->status) {
+       case CALL_STATUS_IDLE:
+               reason = OFONO_DISCONNECT_REASON_UNKNOWN;
+               break;
+
+       case CALL_STATUS_MO_RELEASE:
+               reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+               break;
+
+       case CALL_STATUS_MT_RELEASE:
+               reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+               break;
+
+       case CALL_STATUS_TERMINATED:
+       default:
                reason = OFONO_DISCONNECT_REASON_ERROR;
+               break;
+       }
 
-       DBG("disconnected id=%u reason=%u", call->id, reason);
-       ofono_voicecall_disconnected(ovc, call->id, reason, &error);
-       isi_call_set_idle(call);
+       if (!call->reason) {
+               call->reason = reason;
+               DBG("disconnected id=%u reason=%u", call->id, reason);
+               ofono_voicecall_disconnected(ovc, call->id, reason, &error);
+       }
+
+       if (!reason)
+               isi_call_set_idle(call);
 }
 
-static void isi_call_notify(struct ofono_voicecall *ovc,
-                               struct isi_call *call)
+static void isi_call_notify(struct ofono_voicecall *ovc, struct isi_call *call)
 {
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       struct isi_call_req_context *irc, **queue;
+       struct isi_call_req_ctx *irc, **queue;
        struct ofono_call ocall;
 
        DBG("called with status=%s (0x%02X)",
                call_status_name(call->status), call->status);
 
        for (queue = &ivc->queue; (irc = *queue);) {
-               irc->step(irc, call->id, call->status);
+               irc->step(irc, call->status);
 
                if (*queue == irc)
                        queue = &irc->next;
        }
 
-       ocall = isi_call_as_ofono_call(ivc, call);
-
-       DBG("id=%u,\"%s\",%u,\"%s\",%u,%u",
-               ocall.id,
-               ocall.direction ? "mt" : "mo",
-               ocall.status,
-               ocall.phone_number.number,
-               ocall.phone_number.type,
-               ocall.clip_validity);
-
-       if (ocall.status == CLCC_STATUS_EARLY)
-               return;
-
-       ofono_voicecall_notify(ovc, &ocall);
-
        switch (call->status) {
+       case CALL_STATUS_IDLE:
        case CALL_STATUS_MO_RELEASE:
-               call->reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
-               break;
-
        case CALL_STATUS_MT_RELEASE:
-               call->reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
-               break;
-
-       case CALL_STATUS_IDLE:
        case CALL_STATUS_TERMINATED:
-               isi_call_disconnected(ovc, call);
-               break;
-       }
-}
-
-/* ------------------------------------------------------------------------- */
-/* Decoding subblocks */
-
-static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc,
-                                               struct isi_call *call,
-                                               GIsiSubBlockIter const *sb)
-{
-       uint8_t addr_type, presentation, addr_len;
-       char *address;
-
-       if (!g_isi_sb_iter_get_byte(sb, &addr_type, 2) ||
-               !g_isi_sb_iter_get_byte(sb, &presentation, 3) ||
-               /* fillerbyte */
-               !g_isi_sb_iter_get_byte(sb, &addr_len, 5) ||
-               !g_isi_sb_iter_get_alpha_tag(sb, &address, 2 * addr_len, 6))
-               return;
-
-       call->addr_type = addr_type | 0x80;
-       call->presentation = presentation;
-       strncpy(call->address, address, sizeof call->address);
-
-       g_free(address);
-}
-
-static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc,
-                                               struct isi_call *call,
-                                               GIsiSubBlockIter const *sb)
-{
-       if (!call->address[0])
-               isi_call_any_address_sb_proc(ivc, call, sb);
-}
-
-static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc,
-                                               struct isi_call *call,
-                                               GIsiSubBlockIter const *sb)
-{
-       if (!call->address[0])
-               isi_call_any_address_sb_proc(ivc, call, sb);
-}
-
-static void isi_call_mode_sb_proc(struct isi_voicecall *ivc,
-                                       struct isi_call *call,
-                                       GIsiSubBlockIter const *sb)
-{
-       uint8_t mode, mode_info;
-
-       if (!g_isi_sb_iter_get_byte(sb, &mode, 2) ||
-                       !g_isi_sb_iter_get_byte(sb, &mode_info, 3))
-               return;
-
-       call->mode = mode;
-       call->mode_info = mode_info;
-}
-
-static void isi_call_cause_sb_proc(struct isi_voicecall *ivc,
-                                       struct isi_call *call,
-                                       GIsiSubBlockIter const *sb)
-{
-       uint8_t cause_type, cause;
-
-       if (!g_isi_sb_iter_get_byte(sb, &cause_type, 2) ||
-                       !g_isi_sb_iter_get_byte(sb, &cause, 3))
-               return;
-
-       call->cause_type = cause_type;
-       call->cause = cause;
-}
-
-static void isi_call_status_sb_proc(struct isi_voicecall *ivc,
-                                       struct isi_call *call,
-                                       GIsiSubBlockIter const *sb)
-{
-       uint8_t status;
-
-       if (!g_isi_sb_iter_get_byte(sb, &status, 2))
+               isi_call_release(ovc, call);
                return;
+       }
 
-       call->status = status;
-}
-
-static struct isi_call *
-isi_call_status_info_sb_proc(struct isi_voicecall *ivc,
-                               GIsiSubBlockIter const *sb)
-{
-       struct isi_call *call = NULL;
-       int i;
-       uint8_t call_id;
-       uint8_t mode;
-       uint8_t mode_info;
-       uint8_t status;
-
-       if (!g_isi_sb_iter_get_byte(sb, &call_id, 2) ||
-                       !g_isi_sb_iter_get_byte(sb, &mode, 3) ||
-                       !g_isi_sb_iter_get_byte(sb, &mode_info, 4) ||
-                       !g_isi_sb_iter_get_byte(sb, &status, 5))
-               return NULL;
-
-       i = call_id & 7;
+       ocall = isi_call_as_ofono_call(call);
 
-       if (1 <= i && i <= 7) {
-               call = &ivc->calls[i];
-               call->call_id = call_id;
-               call->status = status;
-               call->mode = mode;
-               call->mode_info = mode_info;
-       }
+       DBG("id=%u,%s,%u,\"%s\",%u,%u",
+               ocall.id,
+               ocall.direction ? "terminated" : "originated",
+               ocall.status,
+               ocall.phone_number.number,
+               ocall.phone_number.type,
+               ocall.clip_validity);
 
-       return call;
+       ofono_voicecall_notify(ovc, &ocall);
 }
 
-static struct isi_call *
-isi_call_addr_and_status_info_sb_proc(struct isi_voicecall *ivc,
-                                       GIsiSubBlockIter const *sb)
+static void isi_call_create_resp(const GIsiMessage *msg, void *data)
 {
-       struct isi_call *call = NULL;
-       int i;
+       struct isi_call_req_ctx *irc = data;
        uint8_t call_id;
-       uint8_t mode;
-       uint8_t mode_info;
-       uint8_t status;
-       uint8_t addr_type;
-       uint8_t presentation;
-       uint8_t addr_len;
-       char *address;
-
-       if (!g_isi_sb_iter_get_byte(sb, &call_id, 2) ||
-               !g_isi_sb_iter_get_byte(sb, &mode, 3) ||
-               !g_isi_sb_iter_get_byte(sb, &mode_info, 4) ||
-               !g_isi_sb_iter_get_byte(sb, &status, 5) ||
-               !g_isi_sb_iter_get_byte(sb, &addr_type, 8) ||
-               !g_isi_sb_iter_get_byte(sb, &presentation, 9) ||
-               !g_isi_sb_iter_get_byte(sb, &addr_len, 11) ||
-               !g_isi_sb_iter_get_alpha_tag(sb, &address, 2 * addr_len, 12))
-               return NULL;
-
-       i = call_id & 7;
 
-       if (1 <= i && i <= 7) {
-               call = &ivc->calls[i];
-               call->call_id = call_id;
-               call->status = status;
-               call->mode = mode;
-               call->mode_info = mode_info;
-               call->addr_type = addr_type | 0x80;
-               call->presentation = presentation;
-               strncpy(call->address, address, sizeof call->address);
+       if (!check_response_status(msg, CALL_CREATE_RESP) ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       call_id == CALL_ID_NONE) {
+               isi_ctx_return_failure(irc);
+               return;
        }
 
-       free(address);
-
-       return call;
+       isi_ctx_return_success(irc);
 }
 
-/* ------------------------------------------------------------------------- */
-/* PN_CALL messages */
-
-static GIsiResponse isi_call_status_resp,
-       isi_call_create_resp,
-       isi_call_answer_resp,
-       isi_call_release_resp,
-       isi_call_control_resp,
-       isi_call_dtmf_send_resp;
-
-static struct isi_call_req_context *
-isi_call_create_req(struct ofono_voicecall *ovc,
-                       uint8_t presentation,
-                       uint8_t addr_type,
-                       char const address[21],
-                       ofono_voicecall_cb_t cb,
-                       void *data)
+static struct isi_call_req_ctx *isi_call_create_req(struct ofono_voicecall *ovc,
+                                                       uint8_t presentation,
+                                                       uint8_t addr_type,
+                                                       char const address[21],
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
 {
        size_t addr_len = strlen(address);
        size_t sub_len = (6 + 2 * addr_len + 3) & ~3;
@@ -573,78 +563,48 @@ isi_call_create_req(struct ofono_voicecall *ovc,
        return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data);
 }
 
-static gboolean isi_call_create_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *irc)
-{
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-
-       if (m != NULL && len < (sizeof *m))
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_CREATE_RESP)
-               return FALSE;
-
-       if (m->call_id != CALL_ID_NONE && m->sub_blocks == 0)
-               return isi_ctx_return_success(irc);
-
-       /* Cause ? */
-       return isi_ctx_return_failure(irc);
-}
-
-static void isi_call_status_ind_cb(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *_ovc)
+static void isi_call_status_ind_cb(const GIsiMessage *msg, void *data)
 {
-       struct ofono_voicecall *ovc = _ovc;
+       struct ofono_voicecall *ovc = data;
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
        struct isi_call *call;
-       uint8_t old;
-       GIsiSubBlockIter sb[1];
+       GIsiSubBlockIter iter;
 
-       if (len < 3)
-               return;         /* runt */
+       uint8_t call_id;
+       uint8_t old_status;
 
-       if ((m->call_id & 7) == 0)
+       if (!ivc || g_isi_msg_id(msg) != CALL_STATUS_IND ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       (call_id & 7) == 0)
                return;
 
-       call = &ivc->calls[m->call_id & 7];
+       call = &ivc->calls[call_id & 7];
+       old_status = call->status;
+       call->call_id = call_id;
 
-       old = call->status;
-       call->call_id = m->call_id;
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
 
-       for (g_isi_sb_iter_init(sb, data, len, (sizeof *m));
-                       g_isi_sb_iter_is_valid(sb);
-                               g_isi_sb_iter_next(sb)) {
-               switch (g_isi_sb_iter_get_id(sb)) {
+               switch (g_isi_sb_iter_get_id(&iter)) {
                case CALL_STATUS:
-                       isi_call_status_sb_proc(ivc, call, sb);
+                       isi_call_status_sb_proc(ivc, call, &iter);
                        break;
 
                case CALL_MODE:
-                       isi_call_mode_sb_proc(ivc, call, sb);
+                       isi_call_mode_sb_proc(ivc, call, &iter);
                        break;
 
                case CALL_CAUSE:
-                       isi_call_cause_sb_proc(ivc, call, sb);
+                       isi_call_cause_sb_proc(ivc, call, &iter);
                        break;
 
                case CALL_DESTINATION_ADDRESS:
-                       isi_call_destination_address_sb_proc(ivc, call, sb);
+                       isi_call_destination_address_sb_proc(ivc, call, &iter);
                        break;
 
                case CALL_ORIGIN_ADDRESS:
-                       isi_call_origin_address_sb_proc(ivc, call, sb);
+                       isi_call_origin_address_sb_proc(ivc, call, &iter);
                        break;
 
                case CALL_GSM_DETAILED_CAUSE:
@@ -657,197 +617,243 @@ static void isi_call_status_ind_cb(GIsiClient *client,
                }
        }
 
-       if (old != call->status)
-               isi_call_notify(ovc, call);
-}
+       if (old_status != call->status) {
 
-static struct isi_call_req_context *
-isi_call_answer_req(struct ofono_voicecall *ovc,
-                       uint8_t call_id, ofono_voicecall_cb_t cb, void *data)
-{
-       uint8_t const req[] = {
-               CALL_ANSWER_REQ, call_id, 0
-       };
-       size_t rlen = sizeof req;
+               if (call->status == CALL_STATUS_IDLE) {
+                       call->status = CALL_STATUS_TERMINATED;
 
-       return isi_call_req(ovc, req, rlen, isi_call_answer_resp, cb, data);
+                       isi_call_notify(ovc, call);
+                       isi_call_set_idle(call);
+                       return;
+               }
+       }
+
+       isi_call_notify(ovc, call);
 }
 
-static gboolean isi_call_answer_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *irc)
+static void isi_call_answer_resp(const GIsiMessage *msg, void *data)
 {
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-
-       if (m != NULL && len < (sizeof *m))
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_ANSWER_RESP)
-               return FALSE;
+       struct isi_call_req_ctx *irc = data;
+       uint8_t call_id;
 
-       if (m->call_id != CALL_ID_NONE && m->sub_blocks == 0)
-               return isi_ctx_return_success(irc);
+       if (!check_response_status(msg, CALL_ANSWER_RESP) ||
+                       !g_isi_msg_data_get_byte(msg, 0, &call_id) ||
+                       call_id == CALL_ID_NONE) {
+               isi_ctx_return_failure(irc);
+               return;
+       }
 
-       /* Cause ? */
-       return isi_ctx_return_failure(irc);
+       isi_ctx_return_success(irc);
 }
 
-static struct isi_call_req_context *
-isi_call_release_req(struct ofono_voicecall *ovc,
-                       uint8_t call_id, uint8_t cause,
-                       ofono_voicecall_cb_t cb, void *data)
+static struct isi_call_req_ctx *isi_call_answer_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
 {
-       uint8_t const req[] = {
-               CALL_RELEASE_REQ, call_id, 2,
-               CALL_CAUSE, 4, CALL_CAUSE_TYPE_CLIENT, cause,
-               CALL_STATE_AUTO_CHANGE, 4, 0, 0
+       const uint8_t req[] = {
+               CALL_ANSWER_REQ,
+               call_id,
+               0
        };
-       size_t rlen = sizeof req;
 
-       return isi_call_req(ovc, req, rlen, isi_call_release_resp, cb, data);
+       return isi_call_req(ovc, req, sizeof(req), isi_call_answer_resp,
+                               cb, data);
 }
 
-static gboolean isi_call_release_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *irc)
+static void isi_call_release_resp(const GIsiMessage *msg, void *data)
 {
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-       GIsiSubBlockIter i[1];
-       uint8_t cause_type = 0, cause = 0;
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause_type;
+       uint8_t cause;
 
-       if (m != NULL && len < (sizeof *m))
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_RELEASE_RESP)
-               return FALSE;
+       if (!check_response_status(msg, CALL_RELEASE_RESP))
+               goto error;
 
-       for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
-                       g_isi_sb_iter_is_valid(i);
-                               g_isi_sb_iter_next(i)) {
-               if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
-                               !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
-                               !g_isi_sb_iter_get_byte(i, &cause, 3))
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
                        continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
        }
 
        if ((cause_type == CALL_CAUSE_TYPE_SERVER ||
                        cause_type == CALL_CAUSE_TYPE_CLIENT) &&
                        (cause == CALL_CAUSE_RELEASE_BY_USER ||
-                       cause == CALL_CAUSE_BUSY_USER_REQUEST))
-               return isi_ctx_return_success(irc);
-       else
-               return isi_ctx_return_failure(irc);
+                       cause == CALL_CAUSE_BUSY_USER_REQUEST)) {
+               isi_ctx_return_success(irc);
+               return;
+       }
+
+error:
+       isi_ctx_return_failure(irc);
 }
 
-static struct isi_call_req_context *
-isi_call_status_req(struct ofono_voicecall *ovc,
-                       uint8_t id, uint8_t mode,
-                       ofono_voicecall_cb_t cb, void *data)
+static struct isi_call_req_ctx *isi_call_release_req(struct ofono_voicecall *ovc,
+                                               uint8_t call_id,
+                                               enum call_cause_type cause_type,
+                                               uint8_t cause,
+                                               ofono_voicecall_cb_t cb,
+                                               void *data)
 {
-       unsigned char req[] = {
-               CALL_STATUS_REQ, id, 1,
-               CALL_STATUS_MODE, 4, mode, 0,
+       const uint8_t req[] = {
+               CALL_RELEASE_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_CAUSE,
+               4,      /* Sub-block length */
+               cause_type,
+               cause,
        };
-       size_t rlen = sizeof req;
 
-       return isi_call_req(ovc, req, rlen, isi_call_status_resp, cb, data);
+       return isi_call_req(ovc, req, sizeof(req), isi_call_release_resp,
+                               cb, data);
 }
 
-
-static gboolean isi_call_status_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *_irc)
+static void isi_call_status_resp(const GIsiMessage *msg, void *data)
 {
-       struct isi_call_req_context *irc = _irc;
+       struct isi_call_req_ctx *irc = data;
        struct ofono_voicecall *ovc = irc->ovc;
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-       GIsiSubBlockIter sb[1];
+       GIsiSubBlockIter iter;
        struct isi_call *call = NULL;
 
-       if (m != NULL && len < (sizeof *m))
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_STATUS_RESP)
-               return FALSE;
+       if (!check_response_status(msg, CALL_STATUS_RESP)) {
+               isi_ctx_return_failure(irc);
+               return;
+       }
 
-       for (g_isi_sb_iter_init(sb, m, len, (sizeof *m));
-                       g_isi_sb_iter_is_valid(sb);
-                               g_isi_sb_iter_next(sb)) {
-               switch (g_isi_sb_iter_get_id(sb)) {
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
 
+               switch (g_isi_sb_iter_get_id(&iter)) {
                case CALL_STATUS_INFO:
-                       call = isi_call_status_info_sb_proc(ivc, sb);
+                       call = isi_call_status_info_sb_proc(ivc, &iter);
                        break;
 
                case CALL_ADDR_AND_STATUS_INFO:
-                       call = isi_call_addr_and_status_info_sb_proc(ivc, sb);
+                       call = isi_call_addr_and_status_info_sb_proc(ivc, &iter);
                        if (call)
                                isi_call_notify(ovc, call);
                        break;
 
                case CALL_CAUSE:
+
                        if (call)
-                               isi_call_cause_sb_proc(ivc, call, sb);
+                               isi_call_cause_sb_proc(ivc, call, &iter);
                        break;
                }
        }
 
-       return isi_ctx_return_success(irc);
+       isi_ctx_return_success(irc);
 }
 
-static struct isi_call_req_context *
-isi_call_control_req(struct ofono_voicecall *ovc,
-                       uint8_t call_id, enum call_operation op, uint8_t info,
-                       ofono_voicecall_cb_t cb, void *data)
+static struct isi_call_req_ctx *isi_call_status_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       uint8_t mode,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
 {
-       uint8_t const req[] = {
-               CALL_CONTROL_REQ, call_id, 1,
-               CALL_OPERATION, 4, op, info,
+       const uint8_t req[] = {
+               CALL_STATUS_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_STATUS_MODE,
+               4,      /* Sub-block length */
+               mode, 0,
        };
-       size_t rlen = sizeof req;
 
-       return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data);
+       return isi_call_req(ovc, req, sizeof(req), isi_call_status_resp,
+                               cb, data);
 }
 
-static struct isi_call_req_context *
-isi_call_deflect_req(struct ofono_voicecall *ovc,
-                       uint8_t call_id, uint8_t address_type,
-                       char const address[21],
-                       ofono_voicecall_cb_t cb, void *data)
+static void isi_call_control_resp(const GIsiMessage *msg, void *data)
+{
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause = CALL_CAUSE_NO_CAUSE;
+       uint8_t cause_type = 0;
+
+       if (!check_response_status(msg, CALL_CONTROL_RESP))
+               goto error;
+
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
+                       continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
+       }
+
+       if (cause == CALL_CAUSE_NO_CAUSE) {
+               isi_ctx_return_failure(irc);
+               return;
+       }
+
+error:
+       isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_ctx *isi_call_control_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       enum call_operation op,
+                                                       uint8_t info,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
+{
+       const uint8_t req[] = {
+               CALL_CONTROL_REQ,
+               call_id,
+               1,      /* Sub-block count */
+               CALL_OPERATION,
+               4,      /* Sub-block length */
+               op, info,
+       };
+
+       return isi_call_req(ovc, req, sizeof(req), isi_call_control_resp,
+                               cb, data);
+}
+
+static struct isi_call_req_ctx *isi_call_deflect_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       uint8_t address_type,
+                                                       const char address[21],
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
 {
        size_t addr_len = strlen(address);
        size_t sub_len = (6 + 2 * addr_len + 3) & ~3;
        size_t i, offset = 3 + 4 + 6;
        size_t rlen = 3 + 4 + sub_len;
        uint8_t req[3 + 4 + 6 + 40] = {
-               CALL_CONTROL_REQ, call_id, 2,
-               CALL_OPERATION, 4, CALL_GSM_OP_DEFLECT, 0,
-               CALL_GSM_DEFLECTION_ADDRESS, sub_len,
+               CALL_CONTROL_REQ,
+               call_id,
+               2,              /* Sub-block count */
+               CALL_OPERATION,
+               4,              /* Sub-block length */
+               CALL_GSM_OP_DEFLECT, 0,
+               CALL_GSM_DEFLECTION_ADDRESS,
+               sub_len,        /* Sub-block lenght */
                address_type & 0x7F,
-               0x7,            /* default presentation */
-               0,              /* filler */
+               0x7,            /* Default presentation */
+               0,              /* Filler */
                addr_len,
        };
 
        if (addr_len > 20) {
                CALLBACK_WITH_FAILURE(cb, data);
-               return FALSE;
+               return NULL;
        }
 
        for (i = 0; i < addr_len; i++)
@@ -856,44 +862,42 @@ isi_call_deflect_req(struct ofono_voicecall *ovc,
        return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data);
 }
 
-static gboolean isi_call_control_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *irc)
+static void isi_call_dtmf_send_resp(const GIsiMessage *msg, void *data)
 {
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-       GIsiSubBlockIter i[1];
-       uint8_t cause_type = 0, cause = 0;
+       struct isi_call_req_ctx *irc = data;
+       GIsiSubBlockIter iter;
+       uint8_t cause_type;
+       uint8_t cause;
 
-       if (m != NULL && len < sizeof *m)
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_CONTROL_RESP)
-               return FALSE;
+       if (!check_response_status(msg, CALL_DTMF_SEND_RESP))
+               goto error;
 
-       for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
-                       g_isi_sb_iter_is_valid(i);
-                               g_isi_sb_iter_next(i)) {
-               if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
-                               !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
-                               !g_isi_sb_iter_get_byte(i, &cause, 3))
+       for (g_isi_sb_iter_init(&iter, msg, 2);
+                       g_isi_sb_iter_is_valid(&iter);
+                       g_isi_sb_iter_next(&iter)) {
+
+               if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
                        continue;
+
+               if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
+                               !g_isi_sb_iter_get_byte(&iter, &cause, 3))
+                       goto error;
+       }
+
+       if (cause == CALL_CAUSE_NO_CAUSE) {
+               isi_ctx_return_success(irc);
+               return;
        }
 
-       if (!cause)
-               return isi_ctx_return_success(irc);
-       else
-               return isi_ctx_return_failure(irc);
+error:
+       isi_ctx_return_failure(irc);
 }
 
-static struct isi_call_req_context *
-isi_call_dtmf_send_req(struct ofono_voicecall *ovc,
-                       uint8_t call_id, char const *string,
-                       ofono_voicecall_cb_t cb, void *data)
+static struct isi_call_req_ctx *isi_call_dtmf_send_req(struct ofono_voicecall *ovc,
+                                                       uint8_t call_id,
+                                                       const char *string,
+                                                       ofono_voicecall_cb_t cb,
+                                                       void *data)
 {
        size_t str_len = strlen(string);
        size_t sub_len = 4 + ((2 * str_len + 3) & ~3);
@@ -923,45 +927,6 @@ isi_call_dtmf_send_req(struct ofono_voicecall *ovc,
        return isi_call_req(ovc, req, rlen, isi_call_dtmf_send_resp, cb, data);
 }
 
-static gboolean isi_call_dtmf_send_resp(GIsiClient *client,
-                                       void const *restrict data,
-                                       size_t len,
-                                       uint16_t object,
-                                       void *irc)
-{
-       struct {
-               uint8_t message_id, call_id, sub_blocks;
-       } const *m = data;
-       GIsiSubBlockIter i[1];
-       uint8_t cause_type = 0, cause = 0;
-
-       if (m != NULL && len < (sizeof *m))
-               return FALSE;
-       if (m == NULL || m->message_id == CALL_COMMON_MESSAGE)
-               return isi_ctx_return_failure(irc);
-       if (m->message_id != CALL_DTMF_SEND_RESP)
-               return FALSE;
-
-       if (m->sub_blocks == 0)
-               return isi_ctx_return_success(irc);
-
-       for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
-                       g_isi_sb_iter_is_valid(i);
-                               g_isi_sb_iter_next(i)) {
-               if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
-                               !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
-                               !g_isi_sb_iter_get_byte(i, &cause, 3))
-                       continue;
-       }
-
-       if (!cause)
-               return isi_ctx_return_success(irc);
-       else
-               return isi_ctx_return_failure(irc);
-}
-
-/* ---------------------------------------------------------------------- */
-
 static void isi_dial(struct ofono_voicecall *ovc,
                        const struct ofono_phone_number *restrict number,
                        enum ofono_clir_option clir,
@@ -995,22 +960,21 @@ static void isi_dial(struct ofono_voicecall *ovc,
                                cb, data);
 }
 
-static void isi_answer(struct ofono_voicecall *ovc,
-                       ofono_voicecall_cb_t cb, void *data)
+static void isi_answer(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb,
+                       void *data)
 {
        isi_call_answer_req(ovc, CALL_ID_ALL, cb, data);
 }
 
 static void isi_hangup_current(struct ofono_voicecall *ovc,
-                       ofono_voicecall_cb_t cb, void *data)
+                               ofono_voicecall_cb_t cb, void *data)
 {
        /*
         * Hangup call(s) that are not held or waiting:
         * active calls or calls in progress.
         */
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       int id;
-       uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
+       int id = 0;
 
        for (id = 1; id <= 7; id++) {
                if (ivc->calls[id].call_id & CALL_ID_WAITING)
@@ -1020,14 +984,11 @@ static void isi_hangup_current(struct ofono_voicecall *ovc,
 
                switch (ivc->calls[id].status) {
                case CALL_STATUS_CREATE:
-               case CALL_STATUS_MT_ALERTING:
-               case CALL_STATUS_ANSWERED:
-                       goto release_by_id;
-
                case CALL_STATUS_COMING:
                case CALL_STATUS_PROCEEDING:
                case CALL_STATUS_MO_ALERTING:
-                       cause = CALL_CAUSE_BUSY_USER_REQUEST;
+               case CALL_STATUS_MT_ALERTING:
+               case CALL_STATUS_ANSWERED:
                        goto release_by_id;
                }
        }
@@ -1035,14 +996,14 @@ static void isi_hangup_current(struct ofono_voicecall *ovc,
        id = CALL_ID_ACTIVE;
 
 release_by_id:
-       isi_call_release_req(ovc, id, cause, cb, data);
+       isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT,
+                               CALL_CAUSE_RELEASE_BY_USER, cb, data);
 }
 
 static void isi_release_all_held(struct ofono_voicecall *ovc,
-                               ofono_voicecall_cb_t cb, void *data)
+                                       ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=0 (w/out incoming calls) */
-       isi_call_release_req(ovc, CALL_ID_HOLD,
+       isi_call_release_req(ovc, CALL_ID_HOLD, CALL_CAUSE_TYPE_CLIENT,
                                CALL_CAUSE_RELEASE_BY_USER, cb, data);
 }
 
@@ -1051,6 +1012,7 @@ static void isi_set_udub(struct ofono_voicecall *ovc,
 {
        /* Release waiting calls */
        isi_call_release_req(ovc, CALL_ID_WAITING,
+                               CALL_CAUSE_TYPE_CLIENT,
                                CALL_CAUSE_BUSY_USER_REQUEST, cb, data);
 }
 
@@ -1060,39 +1022,45 @@ static void isi_retrieve(struct ofono_voicecall *ovc,
        isi_call_control_req(ovc, CALL_ID_HOLD, CALL_OP_RETRIEVE, 0, cb, data);
 }
 
-static void isi_wait_and_answer(struct isi_call_req_context *irc,
-                               int id, int status)
+static void isi_wait_and_answer(struct isi_call_req_ctx *irc, int event)
 {
-       DBG("irc=%p id=%d status=%d", (void *)irc, id, status);
+       DBG("irc=%p event=%u", (void *)irc, event);
 
-       if (id != irc->id)
+       if (event != CALL_STATUS_TERMINATED)
                return;
 
-       switch (status) {
-       case CALL_STATUS_MT_ALERTING:
-               isi_call_answer_req(irc->ovc, irc->id, irc->cb, irc->data);
-               isi_ctx_free(irc);
-               break;
+       isi_answer(irc->ovc, irc->cb, irc->data);
+       isi_ctx_free(irc);
+}
 
-       default:
-               isi_ctx_return_failure(irc);
-               break;
-       }
+static void isi_wait_and_retrieve(struct isi_call_req_ctx *irc, int event)
+{
+       DBG("irc=%p event=%u", (void *)irc, event);
+
+       if (event != CALL_STATUS_TERMINATED)
+               return;
+
+       isi_retrieve(irc->ovc, irc->cb, irc->data);
+       isi_ctx_free(irc);
 }
 
 static void isi_release_all_active(struct ofono_voicecall *ovc,
                                        ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=1 */
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       struct isi_call_req_context *irc;
-       int id;
-       int waiting_id = 0;
+       struct isi_call_req_ctx *irc;
+       int id = 0;
+       int waiting = 0;
        int active = 0;
+       int hold = 0;
 
        for (id = 1; id <= 7; id++) {
+
                if (ivc->calls[id].call_id & CALL_ID_WAITING)
-                       waiting_id = id;
+                       waiting++;
+
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
+                       hold++;
 
                if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
                        active++;
@@ -1104,113 +1072,128 @@ static void isi_release_all_active(struct ofono_voicecall *ovc,
        }
 
        irc = isi_call_release_req(ovc, CALL_ID_ACTIVE,
+                                       CALL_CAUSE_TYPE_CLIENT,
                                        CALL_CAUSE_RELEASE_BY_USER,
                                        cb, data);
-       if (irc == NULL)
+       if (!irc)
                return;
 
-       if (waiting_id)
-               isi_ctx_queue(irc, isi_wait_and_answer, waiting_id);
-
-       /* Retrieving held calls is currently a unwanted side-effect */
+       if (waiting)
+               isi_ctx_queue(irc, isi_wait_and_answer);
+       else if (hold)
+               isi_ctx_queue(irc, isi_wait_and_retrieve);
 }
 
 static void isi_hold_all_active(struct ofono_voicecall *ovc,
                                        ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=2 */
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       int id;
+       int id = 0;
+       int op = 0;
        int waiting = 0;
        int active = 0;
        int hold = 0;
-       int op;
 
        for (id = 1; id <= 7; id++) {
+
                if (ivc->calls[id].call_id & CALL_ID_WAITING)
                        waiting++;
-               else if (ivc->calls[id].call_id & CALL_ID_HOLD)
+
+               if (ivc->calls[id].call_id & CALL_ID_HOLD)
                        hold++;
-               else if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+
+               if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
                        active++;
        }
 
-       if (waiting) {
-               isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data);
+       if (!waiting && !hold && !active) {
+               CALLBACK_WITH_FAILURE(cb, data);
                return;
        }
 
-       if (active) {
-               if (hold)
+       if (waiting) {
+               isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data);
+
+       } else if (hold) {
+
+               if (active) {
                        op = CALL_OP_SWAP;
-               else
-                       op = CALL_OP_HOLD;
+                       id = CALL_ID_ACTIVE;
+               } else {
+                       op = CALL_OP_RETRIEVE;
+                       id = CALL_ID_HOLD;
+               }
+               isi_call_control_req(ovc, id, op, 0, cb, data);
 
-               isi_call_control_req(ovc, CALL_ID_ACTIVE, op, 0, cb, data);
+       } else if (active) {
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_HOLD;
 
-       } else if (hold)
-               isi_retrieve(ovc, cb, data);
-       else
-               CALLBACK_WITH_FAILURE(cb, data);
+               isi_call_control_req(ovc, id, op, 0, cb, data);
+       }
 }
 
 static void isi_release_specific(struct ofono_voicecall *ovc, int id,
                                        ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=1X */
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+       const struct isi_call *status;
+       uint8_t cause;
 
-       if (1 <= id && id <= 7) {
-               struct isi_call const *status = &ivc->calls[id];
-               uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
+       if (id < 1 || id > 7) {
+               CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
 
-               switch (status->status) {
-               case CALL_STATUS_COMING:
-               case CALL_STATUS_MT_ALERTING:
-               case CALL_STATUS_WAITING:
+       status = &ivc->calls[id];
+       cause = CALL_CAUSE_RELEASE_BY_USER;
+
+       switch (status->status) {
+       case CALL_STATUS_MT_ALERTING:
+       case CALL_STATUS_WAITING:
+               cause = CALL_CAUSE_BUSY_USER_REQUEST;
+               break;
+
+       case CALL_STATUS_PROCEEDING:
+
+               if ((status->mode_info & CALL_MODE_ORIGINATOR))
                        cause = CALL_CAUSE_BUSY_USER_REQUEST;
                        break;
-               case CALL_STATUS_PROCEEDING:
-                       if ((status->mode_info & CALL_MODE_ORIGINATOR))
-                               cause = CALL_CAUSE_BUSY_USER_REQUEST;
-                       break;
-               }
+       }
 
-               isi_call_release_req(ovc, id, cause, cb, data);
-       } else
-               CALLBACK_WITH_FAILURE(cb, data);
+       isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data);
 }
 
 static void isi_private_chat(struct ofono_voicecall *ovc, int id,
                                ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=2X */
-       if (1 <= id && id <= 7)
-               isi_call_control_req(ovc, id, CALL_OP_CONFERENCE_SPLIT, 0,
-                                       cb, data);
-       else
+       if (id < 1 || id > 7) {
                CALLBACK_WITH_FAILURE(cb, data);
+               return;
+       }
+
+       isi_call_control_req(ovc, id, CALL_OP_CONFERENCE_SPLIT, 0, cb, data);
 }
 
 static void isi_create_multiparty(struct ofono_voicecall *ovc,
                                        ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=3 */
        isi_call_control_req(ovc, CALL_ID_ALL, CALL_OP_CONFERENCE_BUILD, 0,
                                cb, data);
 }
 
 static void isi_transfer(struct ofono_voicecall *ovc,
-                       ofono_voicecall_cb_t cb, void *data)
+                               ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CHLD=4 */
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-
        uint8_t id;
 
-       for (id = 1; id <= 7; id++)
+       for (id = 1; id <= 7; id++) {
+
                if (ivc->calls[id].status == CALL_STATUS_MO_ALERTING)
                        break;
+       }
+
        if (id > 7)
                id = CALL_ID_ACTIVE;
 
@@ -1221,30 +1204,37 @@ static void isi_deflect(struct ofono_voicecall *ovc,
                        const struct ofono_phone_number *ph,
                        ofono_voicecall_cb_t cb, void *data)
 {
-       /* AT+CTFR=<number>,<type> */
-       int id = CALL_ID_WAITING;
-       isi_call_deflect_req(ovc, id, ph->type, ph->number, cb, data);
+       isi_call_deflect_req(ovc, CALL_ID_WAITING, ph->type, ph->number,
+                               cb, data);
 }
 
 static void isi_swap_without_accept(struct ofono_voicecall *ovc,
-                       ofono_voicecall_cb_t cb, void *data)
+                                       ofono_voicecall_cb_t cb, void *data)
 {
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       int id = 0, op = 0, active = 0, hold = 0;
+       int id = 0;
+       int op = 0;
+       int active = 0;
+       int hold = 0;
 
        for (id = 1; id <= 7; id++) {
+
                if (ivc->calls[id].call_id & CALL_ID_HOLD)
                        hold++;
+
                if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
                        active++;
        }
 
        if (hold && active) {
-               id = CALL_ID_ACTIVE, op = CALL_OP_SWAP;
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_SWAP;
        } else if (active) {
-               id = CALL_ID_ACTIVE, op = CALL_OP_HOLD;
+               id = CALL_ID_ACTIVE;
+               op = CALL_OP_HOLD;
        } else if (hold) {
-               id = CALL_ID_HOLD, op = CALL_OP_RETRIEVE;
+               id = CALL_ID_HOLD;
+               op = CALL_OP_RETRIEVE;
        } else {
                CALLBACK_WITH_FAILURE(cb, data);
                return;
@@ -1259,69 +1249,50 @@ static void isi_send_tones(struct ofono_voicecall *ovc, const char *tones,
        isi_call_dtmf_send_req(ovc, CALL_ID_ALL, tones, cb, data);;
 }
 
-static gboolean isi_call_register(gpointer _ovc)
+static void isi_call_verify_cb(const GIsiMessage *msg, void *data)
 {
-       struct ofono_voicecall *ovc = _ovc;
+       struct ofono_voicecall *ovc = data;
        struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
-       const char *debug = getenv("OFONO_ISI_DEBUG");
 
-       if (debug != NULL && (!strcmp(debug, "all") || !strcmp(debug, "call")))
-               g_isi_client_set_debug(ivc->client, call_debug, NULL);
+       if (g_isi_msg_error(msg) < 0)
+               return;
+
+       ISI_VERSION_DBG(msg);
 
-       g_isi_subscribe(ivc->client,
-                       CALL_STATUS_IND, isi_call_status_ind_cb,
-                       ovc);
+       g_isi_client_ind_subscribe(ivc->client, CALL_STATUS_IND,
+                                       isi_call_status_ind_cb, ovc);
 
-       if (isi_call_status_req(ovc, CALL_ID_ALL,
+       if (!isi_call_status_req(ovc, CALL_ID_ALL,
                                        CALL_STATUS_MODE_ADDR_AND_ORIGIN,
-                                       NULL, NULL) == NULL)
+                                       NULL, NULL))
                DBG("Failed to request call status");
 
        ofono_voicecall_register(ovc);
-
-       return FALSE;
-}
-
-static void isi_call_verify_cb(GIsiClient *client,
-                               gboolean alive, uint16_t object, void *ovc)
-{
-       if (!alive) {
-               DBG("Unable to bootstrap voice call driver");
-               return;
-       }
-
-       DBG("%s (v%03d.%03d) reachable",
-               pn_resource_name(g_isi_client_resource(client)),
-               g_isi_version_major(client),
-               g_isi_version_minor(client));
-
-       g_idle_add(isi_call_register, ovc);
 }
 
 static int isi_voicecall_probe(struct ofono_voicecall *ovc,
                                unsigned int vendor, void *user)
 {
-       GIsiModem *idx = user;
+       GIsiModem *modem = user;
        struct isi_voicecall *ivc;
        int id;
 
        ivc = g_try_new0(struct isi_voicecall, 1);
-       if (ivc == NULL)
+       if (!ivc)
                return -ENOMEM;
 
-       for (id = 1; id <= 7; id++)
+       for (id = 0; id <= 7; id++)
                ivc->calls[id].id = id;
 
-       ivc->client = g_isi_client_create(idx, PN_CALL);
-       if (ivc->client == NULL) {
+       ivc->client = g_isi_client_create(modem, PN_CALL);
+       if (!ivc->client) {
                g_free(ivc);
                return -ENOMEM;
        }
 
        ofono_voicecall_set_data(ovc, ivc);
 
-       if (g_isi_verify(ivc->client, isi_call_verify_cb, ovc) == NULL)
-               DBG("Unable to verify reachability");
+       g_isi_client_verify(ivc->client, isi_call_verify_cb, ovc, NULL);
 
        return 0;
 }
@@ -1330,10 +1301,11 @@ static void isi_voicecall_remove(struct ofono_voicecall *call)
 {
        struct isi_voicecall *data = ofono_voicecall_get_data(call);
 
-       if (data == NULL)
+       ofono_voicecall_set_data(call, NULL);
+
+       if (!data)
                return;
 
-       ofono_voicecall_set_data(call, NULL);
        g_isi_client_destroy(data->client);
        g_free(data);
 }