From cbf6713ed1dceeeea5d6be352ef1aea1cbc4e30e Mon Sep 17 00:00:00 2001 From: Jessica Nilsson Date: Wed, 23 Feb 2011 12:43:21 +0100 Subject: [PATCH] isimodem: wgmodem2.5 added to voicecall --- drivers/isimodem/call.h | 89 +++++++++++++++ drivers/isimodem/voicecall.c | 214 ++++++++++++++++++++++++++++------- 2 files changed, 259 insertions(+), 44 deletions(-) diff --git a/drivers/isimodem/call.h b/drivers/isimodem/call.h index 05f05a5c..0bbef7d4 100644 --- a/drivers/isimodem/call.h +++ b/drivers/isimodem/call.h @@ -27,6 +27,8 @@ extern "C" { #endif #define PN_CALL 0x01 +#define PN_MODEM_CALL 0xC9 +#define CALL_MODEM_PROP_PRESENT_DEFAULT 0x00 enum call_message_id { CALL_CREATE_REQ = 0x01, @@ -333,6 +335,7 @@ enum call_subblock { CALL_MODE_CHANGE_INFO = 0x36, CALL_ADDITIONAL_PARAMS = 0x37, CALL_DSAC_INFO = 0x38, + CALL_LINE_ID = 0x47, }; enum call_id { @@ -351,6 +354,10 @@ enum call_id { CALL_ID_ALL = 0xF0, }; +enum call_dtmf_pause_values { + CALL_DTMF_PAUSE_1S = 0x01 +}; + enum call_mode { CALL_MODE_EMERGENCY = 0x00, CALL_MODE_SPEECH = 0x01, @@ -369,6 +376,12 @@ enum { CALL_GSM_PRESENTATION_DEFAULT = 0x07, }; +enum call_modem_line_id { + CALL_MODEM_PRESENT_DEFAULT = 0x00, + CALL_MODEM_PRESENT_ALLOWED = 0x01, + CALL_MODEM_PRESENT_RESTRICTED = 0x02 +}; + enum call_operation { CALL_OP_HOLD = 0x01, CALL_OP_RETRIEVE = 0x02, @@ -402,6 +415,82 @@ enum { CALL_DTMF_DISABLE_TONE_IND_SEND = 0x02, }; +enum call_notification_indicator { + CALL_NOTIFY_USER_SUSPENDED = 0x00, + CALL_NOTIFY_USER_RESUMED = 0x01, + CALL_NOTIFY_BEARER_CHANGE = 0x02 +}; + +enum call_mmi_ss_codes { + CALL_SSC_ALL_FWDS = 0x0002, + CALL_SSC_ALL_COND_FWD = 0x0004, + CALL_SSC_CFU = 0x0015, + CALL_SSC_CFB = 0x0043, + CALL_SSC_CFNRY = 0x003D, + CALL_SSC_CFGNC = 0x003E, + CALL_SSC_OUTGOING_BARR_SERV = 0x014D, + CALL_SSC_INCOMING_BARR_SERV = 0x0161, + CALL_SSC_CALL_WAITING = 0x002B, + CALL_SSC_CLIR = 0x001F, + CALL_SSC_ETC = 0x0060, + CALL_SSC_MPTY = 0xFFFE, + CALL_SSC_CALL_HOLD = 0xFFFF +}; + +enum call_ss_status { + CALL_SS_STATUS_ACTIVE = 0x01, + CALL_SS_STATUS_REGISTERED = 0x02, + CALL_SS_STATUS_PROVISIONED = 0x04, + CALL_SS_STATUS_QUIESCENT = 0x08 +}; + +enum call_ss_notification { + CALL_SSN_INCOMING_IS_FWD = 0x01, + CALL_SSN_INCOMING_FWD = 0x02, + CALL_SSN_OUTGOING_FWD = 0x04 +}; + +enum call_ss_indicator { + CALL_SSI_CALL_IS_WAITING = 0x01, + CALL_SSI_MPTY = 0x02, + CALL_SSI_CLIR_SUPPR_REJ = 0x04 +}; + +enum call_ss_hold_indicator { + CALL_HOLD_IND_RETRIEVED = 0x00, + CALL_HOLD_IND_ON_HOLD = 0x01 +}; + +enum call_ss_ect_indicator { + CALL_ECT_CALL_STATE_ALERT = 0x00, + CALL_ECT_CALL_STATE_ACTIVE = 0x01 +}; + +enum call_notification_sb_values { + CALL_SB_NOTIFY = 0xB1, + CALL_SB_SS_NOTIFY = 0xB2, + CALL_SB_SS_CODE = 0xB3, + CALL_SB_SS_STATUS = 0xB4, + CALL_SB_SS_NOTIFY_INDICATOR = 0xB5, + CALL_SB_SS_HOLD_INDICATOR = 0xB6, + CALL_SB_SS_ECT_INDICATOR = 0xB7, + CALL_SB_REMOTE_ADDRESS = 0xA6, + CALL_SB_REMOTE_SUBADDRESS = 0xA7, + CALL_SB_CUG_INFO = 0xA0, + CALL_SB_ORIGIN_INFO = 0x0E, + CALL_SB_ALERTING_PATTERN = 0xA1, + CALL_SB_ALERTING_INFO = 0x0C +}; + +/* 27.007 Section 7.7 */ +enum clir_status { + CLIR_STATUS_NOT_PROVISIONED = 0, + CLIR_STATUS_PROVISIONED_PERMANENT, + CLIR_STATUS_UNKNOWN, + CLIR_STATUS_TEMPORARY_RESTRICTED, + CLIR_STATUS_TEMPORARY_ALLOWED +}; + #ifdef __cplusplus }; #endif diff --git a/drivers/isimodem/voicecall.c b/drivers/isimodem/voicecall.c index 08705569..405db013 100644 --- a/drivers/isimodem/voicecall.c +++ b/drivers/isimodem/voicecall.c @@ -84,10 +84,12 @@ struct call_info { struct isi_voicecall { GIsiClient *client; - + GIsiClient *primary; + GIsiClient *secondary; struct isi_call_req_ctx *queue; - struct isi_call calls[8]; + void *control_req_irc; + }; typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason); @@ -588,26 +590,66 @@ static struct isi_call_req_ctx *isi_call_create_req(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb, void *data) { + struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); size_t addr_len = strlen(address); size_t sub_len = (6 + 2 * addr_len + 3) & ~3; - size_t i, offset = 3 + 4 + 8 + 6; - uint8_t req[3 + 4 + 8 + 6 + 40] = { - CALL_CREATE_REQ, - 0, /* No id */ - 3, /* Mode, Clir, Number */ + size_t origin_size = (g_isi_client_resource(ivc->client) == + PN_MODEM_CALL) ? 4 : 8; + size_t i, offset = 3 + 4 + origin_size + 6; + size_t rlen = 3 + 4 + origin_size + sub_len; + uint8_t req[3 + 4 + origin_size + 6 + 40]; + + memset(req, 0, 3 + 4 + origin_size + 6 + 40); + + if (g_isi_client_resource(ivc->client) == PN_MODEM_CALL) { + req[0] = CALL_CREATE_REQ; + req[1] = 0; /* No id */ + req[2] = 3; /* Mode, Clir, Number */ /* MODE SB */ - CALL_MODE, 4, CALL_MODE_SPEECH, CALL_MODE_INFO_NONE, + req[3] = CALL_MODE; + req[4] = 4; + req[5] = CALL_MODE_SPEECH; + req[6] = 0; /* ORIGIN_INFO SB */ - CALL_ORIGIN_INFO, 8, presentation, 0, 0, 0, 0, 0, + req[7] = CALL_LINE_ID; + req[8] = origin_size; + req[9] = presentation; + req[10] = 0; /* DESTINATION_ADDRESS SB */ - CALL_DESTINATION_ADDRESS, - sub_len, - addr_type & 0x7F, - 0, 0, - addr_len, + req[11] = CALL_DESTINATION_ADDRESS; + req[12] = sub_len; + req[13] = addr_type & 0x7F; + req[14] = 0; + req[15] = 0; + req[16] = addr_len; /* uint16_t addr[20] */ - }; - size_t rlen = 3 + 4 + 8 + sub_len; + } else { + req[0] = CALL_CREATE_REQ; + req[1] = 0; /* No id */ + req[2] = 3; /* Mode, Clir, Number */ + /* MODE SB */ + req[3] = CALL_MODE; + req[4] = 4; + req[5] = CALL_MODE_SPEECH; + req[6] = CALL_MODE_INFO_NONE; + /* ORIGIN_INFO SB */ + req[7] = CALL_ORIGIN_INFO; + req[8] = origin_size; + req[9] = presentation; + req[10] = 0; + req[11] = 0; + req[12] = 0; + req[13] = 0; + req[14] = 0; + /* DESTINATION_ADDRESS SB */ + req[15] = CALL_DESTINATION_ADDRESS; + req[16] = sub_len; + req[17] = addr_type & 0x7F; + req[18] = 0; + req[19] = 0; + req[20] = addr_len; + /* uint16_t addr[20] */ + } if (addr_len > 20) { CALLBACK_WITH_FAILURE(cb, data); @@ -772,7 +814,8 @@ error: isi_ctx_return_failure(irc); } -static struct isi_call_req_ctx *isi_call_release_req(struct ofono_voicecall *ovc, +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, @@ -816,7 +859,8 @@ static void isi_call_status_resp(const GIsiMessage *msg, void *data) break; case CALL_ADDR_AND_STATUS_INFO: - call = isi_call_addr_and_status_info_sb_proc(ivc, &iter); + call = isi_call_addr_and_status_info_sb_proc(ivc, + &iter); if (call) isi_call_notify(ovc, call); break; @@ -882,12 +926,13 @@ 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) +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, @@ -902,12 +947,13 @@ static struct isi_call_req_ctx *isi_call_control_req(struct ofono_voicecall *ovc 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) +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; @@ -939,6 +985,37 @@ static struct isi_call_req_ctx *isi_call_deflect_req(struct ofono_voicecall *ovc return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data); } +static void isi_call_control_ind_cb(const GIsiMessage *msg, void *data) +{ + struct ofono_voicecall *ovc = data; + struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); + GIsiSubBlockIter iter; + uint8_t cause_type = 0, cause = 0; + + if (ivc == NULL || g_isi_msg_id(msg) != CALL_CONTROL_IND) + return; + + 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)) + return; + } + + if (ivc->control_req_irc) { + if (!cause) + isi_ctx_return_success(ivc->control_req_irc); + else + isi_ctx_return_failure(ivc->control_req_irc); + + ivc->control_req_irc = NULL; + } +} + static void isi_call_dtmf_send_resp(const GIsiMessage *msg, void *data) { struct isi_call_req_ctx *irc = data; @@ -970,11 +1047,12 @@ error: isi_ctx_return_failure(irc); } -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) +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); @@ -1009,18 +1087,23 @@ static void isi_dial(struct ofono_voicecall *ovc, enum ofono_clir_option clir, ofono_voicecall_cb_t cb, void *data) { - unsigned char presentation = CALL_GSM_PRESENTATION_DEFAULT; + unsigned char presentation; + struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); switch (clir) { - case OFONO_CLIR_OPTION_DEFAULT: - presentation = CALL_GSM_PRESENTATION_DEFAULT; - break; case OFONO_CLIR_OPTION_INVOCATION: presentation = CALL_PRESENTATION_RESTRICTED; break; case OFONO_CLIR_OPTION_SUPPRESSION: presentation = CALL_PRESENTATION_ALLOWED; break; + case OFONO_CLIR_OPTION_DEFAULT: + default: + presentation = (g_isi_client_resource(ivc->client) == + PN_MODEM_CALL) ? + CALL_MODEM_PROP_PRESENT_DEFAULT : + CALL_GSM_PRESENTATION_DEFAULT; + break; } isi_call_create_req(ovc, presentation, number->type, number->number, @@ -1321,7 +1404,7 @@ 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 void isi_call_verify_cb(const GIsiMessage *msg, void *data) +static void isi_primary_call_verify_cb(const GIsiMessage *msg, void *data) { struct ofono_voicecall *ovc = data; struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); @@ -1331,8 +1414,42 @@ static void isi_call_verify_cb(const GIsiMessage *msg, void *data) ISI_VERSION_DBG(msg); + ivc->client = ivc->primary; + g_isi_client_destroy(ivc->secondary); + g_isi_client_ind_subscribe(ivc->client, CALL_STATUS_IND, - isi_call_status_ind_cb, ovc); + isi_call_status_ind_cb, ovc); + g_isi_client_ind_subscribe(ivc->client, CALL_CONTROL_IND, + isi_call_control_ind_cb, ovc); + + g_isi_client_ind_subscribe(ivc->client, CALL_TERMINATED_IND, + isi_call_terminated_ind_cb, ovc); + + if (!isi_call_status_req(ovc, CALL_ID_ALL, + CALL_STATUS_MODE_ADDR_AND_ORIGIN, + NULL, NULL)) + DBG("Failed to request call status"); + + ofono_voicecall_register(ovc); +} + +static void isi_secondary_call_verify_cb(const GIsiMessage *msg, void *data) +{ + struct ofono_voicecall *ovc = data; + struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc); + + if (g_isi_msg_error(msg) < 0) + return; + + ISI_VERSION_DBG(msg); + + ivc->client = ivc->secondary; + g_isi_client_destroy(ivc->primary); + + g_isi_client_ind_subscribe(ivc->client, CALL_STATUS_IND, + isi_call_status_ind_cb, ovc); + g_isi_client_ind_subscribe(ivc->client, CALL_CONTROL_IND, + isi_call_control_ind_cb, ovc); g_isi_client_ind_subscribe(ivc->client, CALL_TERMINATED_IND, isi_call_terminated_ind_cb, ovc); @@ -1359,15 +1476,24 @@ static int isi_voicecall_probe(struct ofono_voicecall *ovc, for (id = 0; id <= 7; id++) ivc->calls[id].id = id; - ivc->client = g_isi_client_create(modem, PN_CALL); - if (ivc->client == NULL) { + ivc->primary = g_isi_client_create(modem, PN_MODEM_CALL); + if (ivc->primary == NULL) { g_free(ivc); return -ENOMEM; } + ivc->secondary = g_isi_client_create(modem, PN_CALL); + if (ivc->secondary == NULL) { + g_free(ivc); + g_isi_client_destroy(ivc->primary); + return -ENOMEM; + } ofono_voicecall_set_data(ovc, ivc); - g_isi_client_verify(ivc->client, isi_call_verify_cb, ovc, NULL); + g_isi_client_verify(ivc->primary, isi_primary_call_verify_cb, ovc, + NULL); + g_isi_client_verify(ivc->secondary, isi_secondary_call_verify_cb, ovc, + NULL); return 0; }