From 346ee5b051d2abc16f55bd60f6846fee78b50c35 Mon Sep 17 00:00:00 2001 From: Aki Niemi Date: Sun, 14 Nov 2010 19:31:06 +0200 Subject: [PATCH] isimodem: Adapt and refactor voicecall driver --- drivers/isimodem/voicecall.c | 1428 +++++++++++++++++----------------- 1 file changed, 700 insertions(+), 728 deletions(-) diff --git a/drivers/isimodem/voicecall.c b/drivers/isimodem/voicecall.c index f2fd53aa..fe4a5725 100644 --- a/drivers/isimodem/voicecall.c +++ b/drivers/isimodem/voicecall.c @@ -28,12 +28,11 @@ #include #include #include -#include #include -#include #include +#include #include #include @@ -45,402 +44,236 @@ #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 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; +}; + +struct call_info { + uint8_t call_id; + uint8_t mode; + uint8_t mode_info; + uint8_t status; }; struct isi_voicecall { GIsiClient *client; - struct isi_call_req_context *queue; + struct isi_call_req_ctx *queue; struct isi_call calls[8]; }; -/* ------------------------------------------------------------------------- */ +typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason); -typedef void GIsiIndication(GIsiClient *client, - const void *restrict data, size_t len, - uint16_t object, void *opaque); - -typedef void GIsiVerify(GIsiClient *client, gboolean alive, uint16_t object, - void *opaque); - -typedef gboolean GIsiResponse(GIsiClient *client, - void const *restrict data, size_t len, - uint16_t object, void *opaque); - -enum { - ISI_CALL_TIMEOUT = 1000, -}; - -/* ------------------------------------------------------------------------- */ -/* Request context for voicecall cb */ - -typedef void isi_call_req_step(struct isi_call_req_context *, - int id, int status); - -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); - - return NULL; } -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_queue(struct isi_call_req_ctx *irc, isi_call_req_step *next) { struct isi_voicecall *ivc; - struct isi_call_req_context *irc; - irc = isi_call_req_new(ovc, cb, data); - if (irc == NULL) - return NULL; + if (irc->prev != NULL) { + irc->step = next; + return; + } - ivc = ofono_voicecall_get_data(ovc); + ivc = ofono_voicecall_get_data(irc->ovc); + if (ivc->queue) { + irc->next = ivc->queue; + irc->next->prev = &irc->next; + } - if (g_isi_send(ivc->client, req, len, - ISI_CALL_TIMEOUT, handler, irc, NULL)) - return irc; + irc->prev = &ivc->queue; + ivc->queue = irc; +} +static void isi_ctx_remove(struct isi_call_req_ctx *irc) +{ + if (!irc->prev) + return; + + *irc->prev = irc->next; + + if (irc->next) { + irc->next->prev = irc->prev; + irc->next = NULL; + } + irc->prev = NULL; +} + +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); - - return NULL; } -static void isi_ctx_queue(struct isi_call_req_context *irc, - isi_call_req_step *next, - int id) +static gboolean isi_ctx_return(struct isi_call_req_ctx *irc, + enum ofono_error_type type, int error) { - if (irc->prev == NULL) { - struct isi_voicecall *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; - } - - irc->step = next; - irc->id = id; -} - -static void isi_ctx_remove(struct isi_call_req_context *irc) -{ - if (irc->prev) { - *irc->prev = irc->next; - - if (irc->next) { - irc->next->prev = irc->prev; - irc->next = NULL; - } - irc->prev = NULL; - } -} - -static void isi_ctx_free(struct isi_call_req_context *irc) -{ - if (irc) { - isi_ctx_remove(irc); - g_free(irc); - } -} - -static gboolean isi_ctx_return(struct isi_call_req_context *irc, - enum ofono_error_type type, - int error) -{ - if (irc == NULL) + if (!irc) return TRUE; if (irc->cb) { - struct ofono_error e = { .type = type, .error = error }; + struct ofono_error e = { + .type = type, + .error = error + }; irc->cb(&e, irc->data); } isi_ctx_free(irc); - return TRUE; } -static gboolean isi_ctx_return_failure(struct isi_call_req_context *irc) +static gboolean isi_ctx_return_failure(struct isi_call_req_ctx *irc) { return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0); } -static gboolean isi_ctx_return_success(struct isi_call_req_context *irc) +static gboolean isi_ctx_return_success(struct isi_call_req_ctx *irc) { - if (irc && irc->step) { - irc->step(irc, 0, 0); - return TRUE; - } + if (!irc || !irc->step) + return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0); - return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0); + irc->step(irc, 0); + return TRUE; } -/* ------------------------------------------------------------------------- */ -/* 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, -}; - -/** Get +CLCC status */ -static int isi_call_status_to_clcc(struct isi_voicecall const *ivc, - struct isi_call const *call) -{ - switch (call->status) { - case CALL_STATUS_CREATE: - return CLCC_STATUS_DIALING; - - case CALL_STATUS_COMING: - return CLCC_STATUS_EARLY; - - case CALL_STATUS_PROCEEDING: - if ((call->mode_info & CALL_MODE_ORIGINATOR)) - return CLCC_STATUS_EARLY; /* MT */ - else - return CLCC_STATUS_DIALING; /* MO */ - - case CALL_STATUS_MO_ALERTING: - return CLCC_STATUS_ALERTING; - - case CALL_STATUS_MT_ALERTING: - return CLCC_STATUS_INCOMING; - - case CALL_STATUS_WAITING: - return CLCC_STATUS_WAITING; - - case CALL_STATUS_ANSWERED: - case CALL_STATUS_ACTIVE: - case CALL_STATUS_HOLD_INITIATED: - case CALL_STATUS_RECONNECT_PENDING: - case CALL_STATUS_SWAP_INITIATED: - return CLCC_STATUS_ACTIVE; - - case CALL_STATUS_HOLD: - case CALL_STATUS_RETRIEVE_INITIATED: - return CLCC_STATUS_HOLD; - - case CALL_STATUS_MO_RELEASE: - case CALL_STATUS_MT_RELEASE: - case CALL_STATUS_TERMINATED: - case CALL_STATUS_IDLE: - return CLCC_STATUS_DISCONNECTED; - } - - return CLCC_STATUS_ACTIVE; -} - -static struct ofono_call isi_call_as_ofono_call(struct isi_voicecall const *ivc, - struct isi_call const *call) -{ - 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); - memcpy(number->number, call->address, sizeof(number->number)); - number->type = 0x80 | call->addr_type; - ocall.clip_validity = call->presentation & 3; - - if (ocall.clip_validity == 0 && strlen(number->number) == 0) - ocall.clip_validity = 2; - - return ocall; -} - -static struct isi_call *isi_call_set_idle(struct isi_call *call) -{ - uint8_t id; - - id = call->id; - memset(call, 0, sizeof(*call)); - call->id = id; - - return call; -} - -static void isi_call_disconnected(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; - - if (!reason) - reason = OFONO_DISCONNECT_REASON_ERROR; - - DBG("disconnected id=%u reason=%u", call->id, reason); - ofono_voicecall_disconnected(ovc, call->id, reason, &error); - isi_call_set_idle(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 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); - - 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_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) + GIsiSubBlockIter *sb) { - uint8_t addr_type, presentation, addr_len; - char *address; + uint8_t type; + uint8_t pres; + uint8_t len; + char *addr; - 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)) + 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; - call->addr_type = addr_type | 0x80; - call->presentation = presentation; - strncpy(call->address, address, sizeof call->address); + call->addr_type = type | 0x80; + call->presentation = pres; + strncpy(call->address, addr, sizeof(call->address)); - g_free(address); + g_free(addr); } static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, - GIsiSubBlockIter const *sb) + GIsiSubBlockIter *sb) { - if (!call->address[0]) + if (call->address[0] == '\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) + struct isi_call *call, + GIsiSubBlockIter *sb) { - if (!call->address[0]) + if (call->address[0] == '\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) + GIsiSubBlockIter *sb) { - uint8_t mode, mode_info; + uint8_t mode; + uint8_t info; if (!g_isi_sb_iter_get_byte(sb, &mode, 2) || - !g_isi_sb_iter_get_byte(sb, &mode_info, 3)) + !g_isi_sb_iter_get_byte(sb, &info, 3)) return; call->mode = mode; - call->mode_info = mode_info; + call->mode_info = info; } static void isi_call_cause_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, - GIsiSubBlockIter const *sb) + GIsiSubBlockIter *sb) { - uint8_t cause_type, cause; + uint8_t type; + uint8_t cause; - if (!g_isi_sb_iter_get_byte(sb, &cause_type, 2) || + if (!g_isi_sb_iter_get_byte(sb, &type, 2) || !g_isi_sb_iter_get_byte(sb, &cause, 3)) return; - call->cause_type = cause_type; + call->cause_type = type; call->cause = cause; } static void isi_call_status_sb_proc(struct isi_voicecall *ivc, struct isi_call *call, - GIsiSubBlockIter const *sb) + GIsiSubBlockIter *sb) { uint8_t status; @@ -450,96 +283,253 @@ static void isi_call_status_sb_proc(struct isi_voicecall *ivc, call->status = status; } -static struct isi_call * -isi_call_status_info_sb_proc(struct isi_voicecall *ivc, - GIsiSubBlockIter const *sb) +static struct isi_call *isi_call_status_info_sb_proc(struct isi_voicecall *ivc, + GIsiSubBlockIter *sb) { struct isi_call *call = NULL; int i; - uint8_t call_id; - uint8_t mode; - uint8_t mode_info; - uint8_t status; + struct call_info *ci; + size_t len = sizeof(struct call_info); - 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)) + if (!g_isi_sb_iter_get_struct(sb, (void *)&ci, len, 2)) return NULL; - i = call_id & 7; + i = ci->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->call_id = ci->call_id; + call->status = ci->status; + call->mode = ci->mode; + call->mode_info = ci->mode_info; } return call; } -static struct isi_call * -isi_call_addr_and_status_info_sb_proc(struct isi_voicecall *ivc, - GIsiSubBlockIter const *sb) +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; - 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; + struct call_addr_info *ci; + size_t len = sizeof(struct call_addr_info); + char *addr; - 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)) + if (!g_isi_sb_iter_get_struct(sb, (void *)&ci, len, 2)) return NULL; - i = call_id & 7; + 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 = 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); + 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); } - free(address); + g_free(addr); + return call; +} + +static int isi_call_status_to_clcc(const struct isi_call *call) +{ + switch (call->status) { + case CALL_STATUS_CREATE: + return 2; + + case CALL_STATUS_COMING: + return 4; + + case CALL_STATUS_PROCEEDING: + + if ((call->mode_info & CALL_MODE_ORIGINATOR)) + return 4; /* MT */ + else + return 2; /* MO */ + + case CALL_STATUS_MO_ALERTING: + return 3; + + case CALL_STATUS_MT_ALERTING: + return 4; + + case CALL_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: + return 0; + + case CALL_STATUS_HOLD: + case CALL_STATUS_RETRIEVE_INITIATED: + return 1; + + case CALL_STATUS_RECONNECT_PENDING: + case CALL_STATUS_TERMINATED: + case CALL_STATUS_SWAP_INITIATED: + return 0; + } + return 0; +} + +static struct ofono_call isi_call_as_ofono_call(const struct isi_call *call) +{ + 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(call); + + memcpy(number->number, call->address, sizeof(number->number)); + + number->type = 0x80 | call->addr_type; + ocall.clip_validity = call->presentation & 3; + + if (ocall.clip_validity == 0 && strlen(number->number) == 0) + ocall.clip_validity = 2; + + 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(struct isi_call)); + call->id = id; return call; } -/* ------------------------------------------------------------------------- */ -/* PN_CALL messages */ +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; -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; + switch (call->status) { + case CALL_STATUS_IDLE: + reason = OFONO_DISCONNECT_REASON_UNKNOWN; + break; -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) + 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; + } + + 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) +{ + struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); + 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->status); + + if (*queue == irc) + queue = &irc->next; + } + + switch (call->status) { + case CALL_STATUS_IDLE: + case CALL_STATUS_MO_RELEASE: + case CALL_STATUS_MT_RELEASE: + case CALL_STATUS_TERMINATED: + isi_call_release(ovc, call); + return; + } + + ocall = isi_call_as_ofono_call(call); + + 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); + + ofono_voicecall_notify(ovc, &ocall); +} + +static void isi_call_create_resp(const GIsiMessage *msg, void *data) +{ + struct isi_call_req_ctx *irc = data; + uint8_t call_id; + + 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; + } + + isi_ctx_return_success(irc); +} + +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) +static void isi_call_status_ind_cb(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_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) -{ - 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) { + + if (call->status == CALL_STATUS_IDLE) { + call->status = CALL_STATUS_TERMINATED; + + isi_call_notify(ovc, call); + isi_call_set_idle(call); + return; + } + } + + isi_call_notify(ovc, call); } -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) +static void isi_call_answer_resp(const GIsiMessage *msg, void *data) { - uint8_t const req[] = { - CALL_ANSWER_REQ, call_id, 0 + struct isi_call_req_ctx *irc = data; + uint8_t call_id; + + 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; + } + + isi_ctx_return_success(irc); +} + +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) +{ + const uint8_t req[] = { + CALL_ANSWER_REQ, + call_id, + 0 }; - size_t rlen = sizeof req; - return isi_call_req(ovc, req, rlen, isi_call_answer_resp, cb, data); + return isi_call_req(ovc, req, sizeof(req), isi_call_answer_resp, + cb, data); } -static gboolean isi_call_answer_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; + 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_ANSWER_RESP) - return FALSE; + if (!check_response_status(msg, CALL_RELEASE_RESP)) + goto error; - if (m->call_id != CALL_ID_NONE && m->sub_blocks == 0) - return isi_ctx_return_success(irc); + for (g_isi_sb_iter_init(&iter, msg, 2); + g_isi_sb_iter_is_valid(&iter); + g_isi_sb_iter_next(&iter)) { - /* Cause ? */ - return isi_ctx_return_failure(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) -{ - 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 - }; - size_t rlen = sizeof req; - - return isi_call_req(ovc, req, rlen, isi_call_release_resp, cb, data); -} - -static gboolean isi_call_release_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_RELEASE_RESP) - return FALSE; - - 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)) + 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) - return isi_ctx_return_success(irc); - else - return isi_ctx_return_failure(irc); + if (cause == CALL_CAUSE_NO_CAUSE) { + isi_ctx_return_success(irc); + return; + } + +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 && !hold && !active) { + CALLBACK_WITH_FAILURE(cb, data); + return; + } + if (waiting) { isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data); - return; - } - if (active) { - if (hold) + } 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=, */ - 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; - g_isi_subscribe(ivc->client, - CALL_STATUS_IND, isi_call_status_ind_cb, - ovc); + ISI_VERSION_DBG(msg); - if (isi_call_status_req(ovc, CALL_ID_ALL, + 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, 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); }