From 6015490d410e820bc3f7feed7a765db3b9538383 Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Tue, 5 Jan 2016 15:35:28 +0200 Subject: [PATCH] [ril] Indicate SIM card presence in appropriate radio state. Contributes to JB#33805 This also reduces the number of GET_SIM_STATUS requests. Only one object per RIL instance makes these requests, the results are shared by all other objects involved. In addition to that, radio power on request is retried if radio power unexpectedly switches off which does happen on multi-sim hardware. --- ofono/Makefile.am | 2 + ofono/drivers/ril/ril_modem.c | 343 ++++++------- ofono/drivers/ril/ril_plugin.c | 131 ++--- ofono/drivers/ril/ril_plugin.h | 34 +- ofono/drivers/ril/ril_radio.c | 395 +++++++++++++++ ofono/drivers/ril/ril_radio.h | 49 ++ ofono/drivers/ril/ril_sim.c | 806 ++++++++++++++----------------- ofono/drivers/ril/ril_sim_card.c | 552 +++++++++++++++++++++ ofono/drivers/ril/ril_sim_card.h | 78 +++ ofono/drivers/ril/ril_sim_dbus.c | 9 +- ofono/drivers/ril/ril_types.h | 11 +- rpm/ofono.spec | 2 +- 12 files changed, 1701 insertions(+), 711 deletions(-) create mode 100644 ofono/drivers/ril/ril_radio.c create mode 100644 ofono/drivers/ril/ril_radio.h create mode 100644 ofono/drivers/ril/ril_sim_card.c create mode 100644 ofono/drivers/ril/ril_sim_card.h diff --git a/ofono/Makefile.am b/ofono/Makefile.am index 9f452eb1d..c9872048d 100644 --- a/ofono/Makefile.am +++ b/ofono/Makefile.am @@ -136,8 +136,10 @@ builtin_sources += drivers/ril/ril_call_barring.c \ drivers/ril/ril_phonebook.c \ drivers/ril/ril_plugin.c \ drivers/ril/ril_plugin_dbus.c \ + drivers/ril/ril_radio.c \ drivers/ril/ril_radio_settings.c \ drivers/ril/ril_sim.c \ + drivers/ril/ril_sim_card.c \ drivers/ril/ril_sim_dbus.c \ drivers/ril/ril_sms.c \ drivers/ril/ril_stk.c \ diff --git a/ofono/drivers/ril/ril_modem.c b/ofono/drivers/ril/ril_modem.c index dca6ce8da..4c80c2f97 100644 --- a/ofono/drivers/ril/ril_modem.c +++ b/ofono/drivers/ril/ril_modem.c @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,13 +14,15 @@ */ #include "ril_plugin.h" -#include "ril_constants.h" +#include "ril_radio.h" +#include "ril_sim_card.h" #include "ril_util.h" #include "ril_log.h" #include "ofono.h" #define MAX_PDP_CONTEXTS (2) +#define ONLINE_TIMEOUT_SECS (15) /* 20 sec is hardcoded in ofono core */ enum ril_modem_power_state { POWERED_OFF, @@ -37,19 +39,18 @@ enum ril_modem_online_state { struct ril_modem_online_request { ofono_modem_online_cb_t cb; + struct ril_modem_data *md; void *data; - guint id; + guint timeout_id; }; -struct ril_modem { - GRilIoChannel *io; +struct ril_modem_data { + struct ril_modem modem; GRilIoQueue *q; - struct ofono_modem *modem; struct ofono_radio_settings *radio_settings; - struct ril_modem_config config; char *default_name; - enum ril_radio_state radio_state; + guint online_check_id; enum ril_modem_power_state power_state; gulong radio_state_event_id; @@ -60,70 +61,64 @@ struct ril_modem { struct ril_modem_online_request set_offline; }; -static guint ril_modem_request_power(struct ril_modem *md, gboolean on, - GRilIoChannelResponseFunc cb); +#define RADIO_POWER_TAG(md) (md) -static inline struct ril_modem *ril_modem_from_ofono(struct ofono_modem *modem) +static struct ril_modem_data *ril_modem_data_from_ofono(struct ofono_modem *o) { - return ofono_modem_get_data(modem); + struct ril_modem_data *md = ofono_modem_get_data(o); + GASSERT(md->modem.ofono == o); + return md; } -GRilIoChannel *ril_modem_io(struct ril_modem *md) +static struct ril_modem_data *ril_modem_data_from_modem(struct ril_modem *m) { - return md ? md->io : NULL; -} - -const struct ril_modem_config *ril_modem_config(struct ril_modem *md) -{ - return md ? &md->config : NULL; -} - -struct ofono_modem *ril_modem_ofono_modem(struct ril_modem *md) -{ - return md ? md->modem : NULL; + return m ? G_CAST(m, struct ril_modem_data, modem) : NULL; } struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *md) { - return (md && md->modem) ? - __ofono_atom_find(OFONO_ATOM_TYPE_SIM, md->modem) : + return (md && md->ofono) ? + __ofono_atom_find(OFONO_ATOM_TYPE_SIM, md->ofono) : NULL; } struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *md) { - return (md && md->modem) ? - __ofono_atom_find(OFONO_ATOM_TYPE_GPRS, md->modem) : + return (md && md->ofono) ? + __ofono_atom_find(OFONO_ATOM_TYPE_GPRS, md->ofono) : NULL; } struct ofono_netreg *ril_modem_ofono_netreg(struct ril_modem *md) { - return (md && md->modem) ? - __ofono_atom_find(OFONO_ATOM_TYPE_NETREG, md->modem) : + return (md && md->ofono) ? + __ofono_atom_find(OFONO_ATOM_TYPE_NETREG, md->ofono) : NULL; } void ril_modem_delete(struct ril_modem *md) { - if (md && md->modem) { - ofono_modem_remove(md->modem); + if (md && md->ofono) { + ofono_modem_remove(md->ofono); } } -void ril_modem_set_removed_cb(struct ril_modem *md, ril_modem_cb_t cb, +void ril_modem_set_removed_cb(struct ril_modem *modem, ril_modem_cb_t cb, void *data) { + struct ril_modem_data *md = ril_modem_data_from_modem(modem); + md->removed_cb = cb; md->removed_cb_data = data; } -void ril_modem_allow_data(struct ril_modem *md) +void ril_modem_allow_data(struct ril_modem *modem) { - if (md && md->modem) { + if (modem) { + struct ril_modem_data *md = ril_modem_data_from_modem(modem); GRilIoRequest *req = grilio_request_sized_new(8); - DBG("%s", ofono_modem_get_path(md->modem)); + DBG("%u", modem->config.slot); grilio_request_append_int32(req, 1); grilio_request_append_int32(req, TRUE); grilio_queue_send_request(md->q, req, RIL_REQUEST_ALLOW_DATA); @@ -131,12 +126,11 @@ void ril_modem_allow_data(struct ril_modem *md) } } -static void ril_modem_online_request_ok(GRilIoChannel* io, - struct ril_modem_online_request *req) +static void ril_modem_online_request_ok(struct ril_modem_online_request *req) { - if (req->id) { - grilio_channel_cancel_request(io, req->id, FALSE); - req->id = 0; + if (req->timeout_id) { + g_source_remove(req->timeout_id); + req->timeout_id = 0; } if (req->cb) { @@ -150,111 +144,112 @@ static void ril_modem_online_request_ok(GRilIoChannel* io, } } -static void ril_modem_update_online_state(struct ril_modem *md) +static void ril_modem_update_online_state(struct ril_modem_data *md) { - switch (md->radio_state) { + switch (md->modem.radio->state) { case RADIO_STATE_ON: - ril_modem_online_request_ok(md->io, &md->set_online); + DBG("online"); + ril_modem_online_request_ok(&md->set_online); break; case RADIO_STATE_OFF: case RADIO_STATE_UNAVAILABLE: - ril_modem_online_request_ok(md->io, &md->set_offline); + DBG("offline"); + ril_modem_online_request_ok(&md->set_offline); break; default: break; } - if (!md->set_offline.id && !md->set_online.id && + if (!md->set_offline.timeout_id && !md->set_online.timeout_id && md->power_state == POWERING_OFF) { md->power_state = POWERED_OFF; - ofono_modem_set_powered(md->modem, FALSE); + if (md->modem.ofono) { + ofono_modem_set_powered(md->modem.ofono, FALSE); + } } } -static void ril_modem_online_request_done(struct ril_modem *md, - struct ril_modem_online_request *req, int ril_status) +static gboolean ril_modem_online_request_timeout(gpointer data) { - GASSERT(req->id); - GASSERT(req->cb); - GASSERT(req->data); - req->id = 0; - - /* If this request has completed successfully, we will - * invoke the callback and notify ofono core when we get - * RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, i.e. the power - * state has actually changed */ - if (ril_status != RIL_E_SUCCESS) { - struct ofono_error error; - ofono_modem_online_cb_t cb = req->cb; - void *data = req->data; - - req->cb = NULL; - req->data = NULL; - cb(ril_error_failure(&error), data); - } - - ril_modem_update_online_state(md); + struct ril_modem_online_request *req = data; + struct ofono_error error; + ofono_modem_online_cb_t cb = req->cb; + void *cb_data = req->data; + + GASSERT(req->timeout_id); + GASSERT(cb); + + req->timeout_id = 0; + req->cb = NULL; + req->data = NULL; + cb(ril_error_failure(&error), cb_data); + ril_modem_update_online_state(req->md); + return FALSE; } -static void ril_modem_set_online_cb(GRilIoChannel *io, int status, - const void *data, guint len, void *user_data) +static gboolean ril_modem_online_check(gpointer data) { - struct ril_modem *md = user_data; + struct ril_modem_data *md = data; - DBG("Power on status %s", ril_error_to_string(status)); - ril_modem_online_request_done(md, &md->set_online, status); + GASSERT(md->online_check_id); + md->online_check_id = 0; + ril_modem_update_online_state(md); + return FALSE; } -static void ril_modem_set_offline_cb(GRilIoChannel *io, int status, - const void *data, guint len, void *user_data) +static void ril_modem_schedule_online_check(struct ril_modem_data *md) { - struct ril_modem *md = user_data; - - DBG("Power on status %s", ril_error_to_string(status)); - ril_modem_online_request_done(md, &md->set_offline, status); + if (!md->online_check_id) { + md->online_check_id = g_idle_add(ril_modem_online_check, md); + } } -static GRilIoRequest *ril_modem_request_radio_power(gboolean on) +static void ril_modem_update_radio_settings(struct ril_modem_data *md) { - GRilIoRequest *req = grilio_request_sized_new(8); - - grilio_request_append_int32(req, 1); - grilio_request_append_int32(req, on); /* Radio ON=1, OFF=0 */ - return req; + if (md->modem.radio->state == RADIO_STATE_ON) { + if (!md->radio_settings) { + DBG("Initializing radio settings interface"); + md->radio_settings = + ofono_radio_settings_create(md->modem.ofono, 0, + RILMODEM_DRIVER, md); + } + } else if (md->radio_settings) { + DBG("Removing radio settings interface"); + ofono_radio_settings_remove(md->radio_settings); + md->radio_settings = NULL; + } } -static guint ril_modem_request_power(struct ril_modem *md, gboolean on, - GRilIoChannelResponseFunc cb) +static void ril_modem_radio_state_cb(struct ril_radio *radio, void *data) { - guint id = 0; - - if (md->q) { - GRilIoRequest *req = ril_modem_request_radio_power(on); - - DBG("[%u] %s", md->config.slot, on ? "ON" : "OFF"); - id = grilio_queue_send_request_full(md->q, req, - RIL_REQUEST_RADIO_POWER, cb, NULL, md); - grilio_request_unref(req); - } + struct ril_modem_data *md = data; - return id; -} + GASSERT(md->modem.radio == radio); + ril_modem_update_radio_settings(md); + ril_modem_update_online_state(md); +}; static void ril_modem_pre_sim(struct ofono_modem *modem) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); DBG(""); ofono_devinfo_create(modem, 0, RILMODEM_DRIVER, md); ofono_sim_create(modem, 0, RILMODEM_DRIVER, md); ofono_voicecall_create(modem, 0, RILMODEM_DRIVER, md); + ril_modem_update_radio_settings(md); + if (!md->radio_state_event_id) { + md->radio_state_event_id = + ril_radio_add_state_changed_handler(md->modem.radio, + ril_modem_radio_state_cb, md); + } } static void ril_modem_post_sim(struct ofono_modem *modem) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); struct ofono_gprs *gprs; struct ofono_gprs_context *gc; int i; @@ -282,7 +277,7 @@ static void ril_modem_post_sim(struct ofono_modem *modem) static void ril_modem_post_online(struct ofono_modem *modem) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); DBG(""); ofono_call_volume_create(modem, 0, RILMODEM_DRIVER, md); @@ -295,36 +290,33 @@ static void ril_modem_post_online(struct ofono_modem *modem) static void ril_modem_set_online(struct ofono_modem *modem, ofono_bool_t online, ofono_modem_online_cb_t cb, void *data) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); struct ril_modem_online_request *req; DBG("%s going %sline", ofono_modem_get_path(modem), online ? "on" : "off"); if (online) { + ril_radio_power_on(md->modem.radio, RADIO_POWER_TAG(md)); req = &md->set_online; - GASSERT(!req->id); - req->id = ril_modem_request_power(md, TRUE, - ril_modem_set_online_cb); } else { + ril_radio_power_off(md->modem.radio, RADIO_POWER_TAG(md)); req = &md->set_offline; - GASSERT(!req->id); - req->id = ril_modem_request_power(md, FALSE, - ril_modem_set_offline_cb); } - if (req->id) { - req->cb = cb; - req->data = data; - } else { - struct ofono_error error; - cb(ril_error_failure(&error), data); + req->cb = cb; + req->data = data; + if (req->timeout_id) { + g_source_remove(req->timeout_id); } + req->timeout_id = g_timeout_add_seconds(ONLINE_TIMEOUT_SECS, + ril_modem_online_request_timeout, req); + ril_modem_schedule_online_check(md); } static int ril_modem_enable(struct ofono_modem *modem) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); DBG("%s", ofono_modem_get_path(modem)); md->power_state = POWERED_ON; @@ -333,10 +325,10 @@ static int ril_modem_enable(struct ofono_modem *modem) static int ril_modem_disable(struct ofono_modem *modem) { - struct ril_modem *md = ril_modem_from_ofono(modem); + struct ril_modem_data *md = ril_modem_data_from_ofono(modem); DBG("%s", ofono_modem_get_path(modem)); - if (md->set_online.id || md->set_offline.id) { + if (md->set_online.timeout_id || md->set_offline.timeout_id) { md->power_state = POWERING_OFF; return -EINPROGRESS; } else { @@ -351,108 +343,89 @@ static int ril_modem_probe(struct ofono_modem *modem) return 0; } -static void ril_modem_remove(struct ofono_modem *modem) +static void ril_modem_remove(struct ofono_modem *ofono) { - struct ril_modem *md = ril_modem_from_ofono(modem); - - DBG("%s", ofono_modem_get_path(modem)); - GASSERT(md->modem); - - if (md->radio_state > RADIO_STATE_UNAVAILABLE) { - GRilIoRequest *req = ril_modem_request_radio_power(FALSE); - grilio_channel_send_request(md->io, req, - RIL_REQUEST_RADIO_POWER); - grilio_request_unref(req); - } + struct ril_modem_data *md = ril_modem_data_from_ofono(ofono); + struct ril_modem *modem = &md->modem; + DBG("%s", ril_modem_get_path(modem)); if (md->removed_cb) { ril_modem_cb_t cb = md->removed_cb; void *data = md->removed_cb_data; md->removed_cb = NULL; md->removed_cb_data = NULL; - cb(md, data); + cb(modem, data); + } + + ofono_modem_set_data(ofono, NULL); + + ril_radio_remove_handler(modem->radio, md->radio_state_event_id); + ril_radio_power_off(modem->radio, RADIO_POWER_TAG(md)); + ril_radio_unref(modem->radio); + + if (md->online_check_id) { + g_source_remove(md->online_check_id); } - ofono_modem_set_data(modem, NULL); + if (md->set_online.timeout_id) { + g_source_remove(md->set_online.timeout_id); + } + + if (md->set_offline.timeout_id) { + g_source_remove(md->set_offline.timeout_id); + } - grilio_channel_remove_handler(md->io, md->radio_state_event_id); - grilio_channel_unref(md->io); + ril_sim_card_unref(modem->sim_card); + grilio_channel_unref(modem->io); grilio_queue_cancel_all(md->q, FALSE); grilio_queue_unref(md->q); g_free(md->default_name); g_free(md); } -static void ril_modem_radio_state_changed(GRilIoChannel *io, guint ril_event, - const void *data, guint len, void *user_data) +struct ril_modem *ril_modem_create(GRilIoChannel *io, struct ril_radio *radio, + struct ril_sim_card *sc, const char *dev, + const struct ril_slot_config *config) { - struct ril_modem *md = user_data; - GRilIoParser rilp; - int radio_state; - - GASSERT(ril_event == RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED); - grilio_parser_init(&rilp, data, len); - if (grilio_parser_get_int32(&rilp, &radio_state) && - grilio_parser_at_end(&rilp)) { - DBG("%s %s", ofono_modem_get_path(md->modem), - ril_radio_state_to_string(radio_state)); - md->radio_state = radio_state; - if (radio_state == RADIO_STATE_ON && !md->radio_settings) { - DBG("Initializing radio settings interface"); - md->radio_settings = - ofono_radio_settings_create(md->modem, 0, - RILMODEM_DRIVER, md); - } + struct ofono_modem *ofono = ofono_modem_create(dev, RILMODEM_DRIVER); - ril_modem_update_online_state(md); - } else { - ofono_error("Error parsing RADIO_STATE_CHANGED"); - } -} - -struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *dev, - const struct ril_modem_config *config) -{ - struct ofono_modem *modem = ofono_modem_create(dev, RILMODEM_DRIVER); - - if (modem) { + if (ofono) { int err; - struct ril_modem *md = g_new0(struct ril_modem, 1); + struct ril_modem_data *md = g_new0(struct ril_modem_data, 1); + struct ril_modem *modem = &md->modem; /* Copy config */ - md->config = *config; + modem->config = *config; if (config->default_name && config->default_name[0]) { md->default_name = g_strdup(config->default_name); } else { md->default_name = g_strdup_printf("SIM%u", config->slot + 1); } - md->config.default_name = md->default_name; + modem->config.default_name = md->default_name; - md->modem = modem; - md->io = grilio_channel_ref(io); + modem->ofono = ofono; + modem->radio = ril_radio_ref(radio); + modem->sim_card = ril_sim_card_ref(sc); + modem->io = grilio_channel_ref(io); md->q = grilio_queue_new(io); - ofono_modem_set_data(modem, md); - err = ofono_modem_register(modem); + md->set_online.md = md; + md->set_offline.md = md; + ofono_modem_set_data(ofono, md); + err = ofono_modem_register(ofono); if (!err) { - md->radio_state_event_id = - grilio_channel_add_unsol_event_handler(io, - ril_modem_radio_state_changed, - RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, - md); - + ril_radio_power_on(modem->radio, RADIO_POWER_TAG(md)); GASSERT(io->connected); - ril_modem_request_power(md, FALSE, NULL); /* * ofono_modem_reset sets Powered to TRUE without * issuing PropertyChange signal. */ - ofono_modem_set_powered(md->modem, FALSE); - ofono_modem_set_powered(md->modem, TRUE); + ofono_modem_set_powered(modem->ofono, FALSE); + ofono_modem_set_powered(modem->ofono, TRUE); md->power_state = POWERED_ON; - return md; + return modem; } else { ofono_error("Error %d registering %s", err, RILMODEM_DRIVER); @@ -462,10 +435,10 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *dev, * ofono_modem_remove() won't invoke * ril_modem_remove() callback. */ - ril_modem_remove(modem); + ril_modem_remove(ofono); } - ofono_modem_remove(modem); + ofono_modem_remove(ofono); } return NULL; diff --git a/ofono/drivers/ril/ril_plugin.c b/ofono/drivers/ril/ril_plugin.c index e640c665e..f90f4f3fe 100644 --- a/ofono/drivers/ril/ril_plugin.c +++ b/ofono/drivers/ril/ril_plugin.c @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,8 +14,9 @@ */ #include "ril_plugin.h" +#include "ril_sim_card.h" +#include "ril_radio.h" #include "ril_mce.h" -#include "ril_constants.h" #include "ril_util.h" #include "ril_log.h" @@ -63,7 +64,6 @@ enum ril_plugin_io_events { IO_EVENT_CONNECTED, IO_EVENT_ERROR, IO_EVENT_EOF, - IO_EVENT_SIM_STATUS, IO_EVENT_COUNT }; @@ -88,16 +88,19 @@ struct ril_slot { char *sub; gint timeout; /* RIL timeout, in seconds */ int index; - struct ril_modem_config config; + struct ril_slot_config config; struct ril_plugin_priv *plugin; struct ril_sim_dbus *sim_dbus; struct ril_modem *modem; struct ril_mce *mce; struct ofono_sim *sim; + struct ril_radio *radio; + struct ril_sim_card *sim_card; GRilIoChannel *io; gulong io_event_id[IO_EVENT_COUNT]; - gulong sim_status_req_id; gulong imei_req_id; + gulong sim_card_state_event_id; + gulong radio_state_event_id; guint trace_id; guint dump_id; guint retry_id; @@ -160,7 +163,7 @@ static void ril_plugin_shutdown_slot(struct ril_slot *slot, gboolean kill_io) } if (slot->modem) { - struct ofono_modem *m = ril_modem_ofono_modem(slot->modem); + struct ofono_modem *m = slot->modem->ofono; if (m && slot->sim_watch_id) { __ofono_modem_remove_atom_watch(m, slot->sim_watch_id); @@ -198,10 +201,7 @@ static void ril_plugin_shutdown_slot(struct ril_slot *slot, gboolean kill_io) grilio_channel_cancel_request(slot->io, slot->imei_req_id, FALSE); - grilio_channel_cancel_request(slot->io, - slot->sim_status_req_id, FALSE); slot->imei_req_id = 0; - slot->sim_status_req_id = 0; for (i=0; iio, FALSE); grilio_channel_unref(slot->io); slot->io = NULL; + + ril_radio_remove_handler(slot->radio, + slot->radio_state_event_id); + ril_radio_unref(slot->radio); + slot->radio = NULL; + + ril_sim_card_remove_handler(slot->sim_card, + slot->sim_card_state_event_id); + ril_sim_card_unref(slot->sim_card); + slot->sim_card_state_event_id = 0; + slot->sim_card = NULL; } } } @@ -342,55 +353,27 @@ static void ril_plugin_check_sim_state(struct ril_slot *slot) } } -static void ril_plugin_request_sim_status_cb(GRilIoChannel *io, int err, - const void *data, guint len, void *user_data) +static void ril_plugin_sim_state_changed(struct ril_sim_card *card, void *data) { - struct ril_slot *slot = user_data; + struct ril_slot *slot = data; + gboolean present; - slot->sim_status_req_id = 0; - if (err != RIL_E_SUCCESS) { - ofono_error("SIM status error %s", ril_error_to_string(err)); + if (card && card->status && + card->status->card_state == RIL_CARDSTATE_PRESENT) { + DBG("SIM found in slot %u", slot->config.slot); + present = TRUE; } else { - GRilIoParser rilp; - guint32 cardstate; - gboolean present; - - grilio_parser_init(&rilp, data, len); - if (grilio_parser_get_uint32(&rilp, &cardstate) && - (cardstate == RIL_CARDSTATE_PRESENT)) { - DBG("SIM found in slot %u", slot->config.slot); - present = TRUE; - } else { - DBG("No SIM in slot %u", slot->config.slot); - present = FALSE; - } + DBG("No SIM in slot %u", slot->config.slot); + present = FALSE; + } - if (slot->pub.sim_present != present) { - slot->pub.sim_present = present; - ril_plugin_dbus_signal_sim(slot->plugin->dbus, + if (slot->pub.sim_present != present) { + slot->pub.sim_present = present; + ril_plugin_dbus_signal_sim(slot->plugin->dbus, slot->index, present); - } } } -static void ril_plugin_request_sim_status(struct ril_slot *slot) -{ - grilio_channel_cancel_request(slot->io, slot->sim_status_req_id, FALSE); - slot->sim_status_req_id = grilio_channel_send_request_full(slot->io, - NULL, RIL_REQUEST_GET_SIM_STATUS, - ril_plugin_request_sim_status_cb, NULL, slot); -} - -static void ril_plugin_slot_status_changed(GRilIoChannel *io, guint code, - const void *data, guint len, void *user_data) -{ - struct ril_slot *slot = user_data; - - DBG("%s", slot->path); - GASSERT(code == RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); - ril_plugin_request_sim_status(slot); -} - static void ril_plugin_sim_watch_done(void *data) { struct ril_slot *slot = data; @@ -563,14 +546,14 @@ static void ril_plugin_create_modem(struct ril_slot *slot) GASSERT(slot->io && slot->io->connected); GASSERT(!slot->modem); - modem = ril_modem_create(slot->io, slot->path + 1, &slot->config); + modem = ril_modem_create(slot->io, slot->radio, slot->sim_card, + slot->path + 1, &slot->config); if (modem) { struct ofono_sim *sim = ril_modem_ofono_sim(modem); slot->modem = modem; - slot->sim_watch_id = __ofono_modem_add_atom_watch( - ril_modem_ofono_modem(modem), + slot->sim_watch_id = __ofono_modem_add_atom_watch(modem->ofono, OFONO_ATOM_TYPE_SIM, ril_plugin_sim_watch, slot, ril_plugin_sim_watch_done); if (sim) { @@ -617,9 +600,31 @@ static void ril_plugin_imei_cb(GRilIoChannel *io, int status, } } +static void ril_plugin_power_check(struct ril_slot *slot) +{ + /* + * It seems to be necessary to kick (with RIL_REQUEST_RADIO_POWER) + * the modems with power on after one of the modems has been powered + * off. Otherwise bad things may happens (like the modem never + * registering on the network). + */ + ril_radio_confirm_power_on(slot->radio); +} + +static void ril_plugin_radio_state_changed(struct ril_radio *radio, void *data) +{ + struct ril_slot *slot = data; + + if (radio->state == RADIO_STATE_OFF) { + DBG("power off for slot %u", slot->config.slot); + ril_plugin_foreach_slot(slot->plugin, ril_plugin_power_check); + } +} + static void ril_plugin_slot_connected(struct ril_slot *slot) { - ofono_debug("%s version %u", slot->name, slot->io->ril_version); + ofono_debug("%s version %u", (slot->name && slot->name[0]) ? + slot->name : "RIL", slot->io->ril_version); GASSERT(slot->io->connected); GASSERT(!slot->io_event_id[IO_EVENT_CONNECTED]); @@ -631,7 +636,18 @@ static void ril_plugin_slot_connected(struct ril_slot *slot) slot->imei_req_id = grilio_channel_send_request_full(slot->io, NULL, RIL_REQUEST_GET_IMEI, ril_plugin_imei_cb, NULL, slot); - ril_plugin_request_sim_status(slot); + GASSERT(!slot->radio); + GASSERT(!slot->radio_state_event_id); + slot->radio = ril_radio_new(slot->io); + slot->radio_state_event_id = + ril_radio_add_state_changed_handler(slot->radio, + ril_plugin_radio_state_changed, slot); + + GASSERT(!slot->sim_card); + slot->sim_card = ril_sim_card_new(slot->io, slot->config.slot); + slot->sim_card_state_event_id = ril_sim_card_add_state_changed_handler( + slot->sim_card, ril_plugin_sim_state_changed, slot); + if (ril_plugin_can_create_modem(slot) && !slot->modem) { ril_plugin_create_modem(slot); } @@ -665,11 +681,6 @@ static void ril_plugin_init_io(struct ril_slot *slot) slot->io_event_id[IO_EVENT_EOF] = grilio_channel_add_disconnected_handler(slot->io, ril_plugin_slot_disconnected, slot); - slot->io_event_id[IO_EVENT_SIM_STATUS] = - grilio_channel_add_unsol_event_handler(slot->io, - ril_plugin_slot_status_changed, - RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, - slot); if (slot->io->connected) { ril_plugin_slot_connected(slot); diff --git a/ofono/drivers/ril/ril_plugin.h b/ofono/drivers/ril/ril_plugin.h index 64d7f4ca4..593c65e49 100644 --- a/ofono/drivers/ril/ril_plugin.h +++ b/ofono/drivers/ril/ril_plugin.h @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -42,8 +42,6 @@ #include #define RILMODEM_DRIVER "ril" -#define RIL_RETRY_SECS (2) -#define MAX_SIM_STATUS_RETRIES (15) struct ril_slot_info { const char *path; @@ -60,20 +58,26 @@ struct ril_plugin { const struct ril_slot_info **slots; }; -struct ril_modem_config { +struct ril_slot_config { guint slot; gboolean enable_4g; const char *default_name; }; +struct ril_modem { + GRilIoChannel *io; + struct ofono_modem *ofono; + struct ril_radio *radio; + struct ril_sim_card *sim_card; + struct ril_slot_config config; +}; + #define RIL_PLUGIN_SIGNAL_VOICE_IMSI (0x01) #define RIL_PLUGIN_SIGNAL_DATA_IMSI (0x02) #define RIL_PLUGIN_SIGNAL_VOICE_PATH (0x04) #define RIL_PLUGIN_SIGNAL_DATA_PATH (0x10) #define RIL_PLUGIN_SIGNAL_ENABLED_SLOTS (0x20) -struct ril_modem; -struct ril_plugin_dbus; typedef void (*ril_modem_cb_t)(struct ril_modem *modem, void *data); void ril_plugin_set_enabled_slots(struct ril_plugin *plugin, char **slots); @@ -94,22 +98,21 @@ void ril_plugin_dbus_signal(struct ril_plugin_dbus *dbus, int mask); void ril_plugin_dbus_signal_sim(struct ril_plugin_dbus *dbus, int index, gboolean present); -struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *dev, - const struct ril_modem_config *config); +struct ril_modem *ril_modem_create(GRilIoChannel *io, struct ril_radio *radio, + struct ril_sim_card *sc, const char *dev, + const struct ril_slot_config *config); void ril_modem_delete(struct ril_modem *modem); void ril_modem_allow_data(struct ril_modem *modem); -GRilIoChannel *ril_modem_io(struct ril_modem *modem); -const struct ril_modem_config *ril_modem_config(struct ril_modem *modem); struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *modem); struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *modem); struct ofono_netreg *ril_modem_ofono_netreg(struct ril_modem *modem); -struct ofono_modem *ril_modem_ofono_modem(struct ril_modem *modem); void ril_modem_set_removed_cb(struct ril_modem *modem, ril_modem_cb_t cb, void *data); -#define ril_modem_slot(md) (ril_modem_config(modem)->slot) -#define ril_modem_4g_enabled(md) (ril_modem_config(modem)->enable_4g) -#define ril_modem_get_path(md) ofono_modem_get_path(ril_modem_ofono_modem(md)) +#define ril_modem_get_path(modem) ofono_modem_get_path((modem)->ofono) +#define ril_modem_4g_enabled(modem) ((modem)->config.enable_4g) +#define ril_modem_slot(modem) ((modem)->config.slot) +#define ril_modem_io(modem) ((modem)->io) void ril_sim_read_file_linear(struct ofono_sim *sim, int fileid, int record, int length, const unsigned char *path, @@ -126,8 +129,7 @@ void ril_sim_read_file_info(struct ofono_sim *sim, int fileid, int ril_sim_app_type(struct ofono_sim *sim); int ril_gprs_ril_data_tech(struct ofono_gprs *gprs); -int ril_netreg_check_if_really_roaming(struct ofono_netreg *netreg, - gint status); +int ril_netreg_check_if_really_roaming(struct ofono_netreg *netreg, gint status); extern const struct ofono_call_barring_driver ril_call_barring_driver; extern const struct ofono_call_forwarding_driver ril_call_forwarding_driver; diff --git a/ofono/drivers/ril/ril_radio.c b/ofono/drivers/ril/ril_radio.c new file mode 100644 index 000000000..61936b6a8 --- /dev/null +++ b/ofono/drivers/ril/ril_radio.c @@ -0,0 +1,395 @@ +/* + * oFono - Open Source Telephony - RIL-based devices + * + * Copyright (C) 2015-2016 Jolla Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ril_radio.h" +#include "ril_util.h" +#include "ril_log.h" + +#include +#include +#include + +typedef GObjectClass RilRadioClass; +typedef struct ril_radio RilRadio; + +/* + * Object states: + * + * 1. Idle (!pending && !retry) + * 2. Power on/off request pending (pending) + * 3. Power on retry has been scheduled (retry) + */ +struct ril_radio_priv { + GRilIoChannel *io; + GRilIoQueue *q; + gulong state_event_id; + char *log_prefix; + GHashTable *req_table; + guint pending_id; + guint retry_id; + guint state_changed_while_request_pending; + enum ril_radio_state last_known_state; + gboolean power_cycle; + gboolean next_state_valid; + gboolean next_state; +}; + +enum ril_radio_signal { + SIGNAL_STATE_CHANGED, + SIGNAL_COUNT +}; + +#define POWER_RETRY_SECS (1) + +#define SIGNAL_STATE_CHANGED_NAME "ril-radio-state-changed" + +static guint ril_radio_signals[SIGNAL_COUNT] = { 0 }; + +G_DEFINE_TYPE(RilRadio, ril_radio, G_TYPE_OBJECT) +#define RIL_RADIO_TYPE (ril_radio_get_type()) +#define RIL_RADIO(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj,RIL_RADIO_TYPE,RilRadio)) + +static void ril_radio_submit_power_request(struct ril_radio *self, gboolean on); + +G_INLINE_FUNC gboolean ril_radio_power_should_be_on(struct ril_radio *self) +{ + struct ril_radio_priv *priv = self->priv; + + return g_hash_table_size(priv->req_table) && !priv->power_cycle; +} + +G_INLINE_FUNC gboolean ril_radio_state_off(enum ril_radio_state radio_state) +{ + return radio_state == RADIO_STATE_OFF; +} + +G_INLINE_FUNC gboolean ril_radio_state_on(enum ril_radio_state radio_state) +{ + return !ril_radio_state_off(radio_state); +} + +static gboolean ril_radio_power_request_retry_cb(gpointer user_data) +{ + struct ril_radio *self = user_data; + struct ril_radio_priv *priv = self->priv; + + DBG("%s", priv->log_prefix); + GASSERT(priv->retry_id); + priv->retry_id = 0; + ril_radio_submit_power_request(self, ril_radio_power_should_be_on(self)); + return FALSE; +} + +static void ril_radio_cancel_retry(struct ril_radio *self) +{ + struct ril_radio_priv *priv = self->priv; + + if (priv->retry_id) { + DBG("%sretry cancelled", priv->log_prefix); + g_source_remove(priv->retry_id); + priv->retry_id = 0; + } +} + +static void ril_radio_check_state(struct ril_radio *self) +{ + struct ril_radio_priv *priv = self->priv; + + if (!priv->pending_id) { + const gboolean should_be_on = ril_radio_power_should_be_on(self); + + if (ril_radio_state_on(self->priv->last_known_state) == + should_be_on) { + /* All is good, cancel pending retry if there is one */ + ril_radio_cancel_retry(self); + } else if (priv->state_changed_while_request_pending) { + /* Hmm... RIL's reaction was inadequate, repeat */ + ril_radio_submit_power_request(self, should_be_on); + } else if (!priv->retry_id) { + /* There has been no reaction so far, wait a bit */ + DBG("%sretry scheduled", priv->log_prefix); + priv->retry_id = g_timeout_add_seconds(POWER_RETRY_SECS, + ril_radio_power_request_retry_cb, self); + } + } + + /* Don't update public state while something is pending */ + if (!priv->pending_id && !priv->retry_id && + self->state != priv->last_known_state) { + DBG("%s%s -> %s", priv->log_prefix, + ril_radio_state_to_string(self->state), + ril_radio_state_to_string(priv->last_known_state)); + self->state = priv->last_known_state; + g_signal_emit(self, ril_radio_signals[SIGNAL_STATE_CHANGED], 0); + } +} + +static void ril_radio_power_request_cb(GRilIoChannel *channel, int ril_status, + const void *data, guint len, void *user_data) +{ + struct ril_radio *self = user_data; + struct ril_radio_priv *priv = self->priv; + + GASSERT(priv->pending_id); + priv->pending_id = 0; + + if (ril_status != RIL_E_SUCCESS) { + ofono_error("Power request failed: %s", + ril_error_to_string(ril_status)); + } + + if (priv->next_state_valid) { + ril_radio_submit_power_request(self, priv->next_state); + } else { + ril_radio_check_state(self); + } +} + +static void ril_radio_submit_power_request(struct ril_radio *self, gboolean on) +{ + struct ril_radio_priv *priv = self->priv; + GRilIoRequest *req = grilio_request_sized_new(8); + + grilio_request_append_int32(req, 1); + grilio_request_append_int32(req, on); /* Radio ON=1, OFF=0 */ + + priv->next_state_valid = FALSE; + priv->next_state = on; + priv->state_changed_while_request_pending = 0; + ril_radio_cancel_retry(self); + + GASSERT(!priv->pending_id); + priv->pending_id = grilio_queue_send_request_full(priv->q, req, + RIL_REQUEST_RADIO_POWER, ril_radio_power_request_cb, NULL, self); + grilio_request_unref(req); +} + +static void ril_radio_power_request(struct ril_radio *self, gboolean on, + gboolean allow_repeat) +{ + struct ril_radio_priv *priv = self->priv; + const char *on_off = on ? "on" : "off"; + + if (priv->pending_id) { + if (allow_repeat || priv->next_state != on) { + /* Wait for the pending request to complete */ + priv->next_state_valid = TRUE; + priv->next_state = on; + DBG("%s%s (queued)", priv->log_prefix, on_off); + } else { + DBG("%s%s (ignored)", priv->log_prefix, on_off); + } + } else { + DBG("%s%s", priv->log_prefix, on_off); + ril_radio_submit_power_request(self, on); + } +} + +void ril_radio_confirm_power_on(struct ril_radio *self) +{ + if (G_LIKELY(self) && ril_radio_power_should_be_on(self)) { + ril_radio_power_request(self, TRUE, TRUE); + } +} + +void ril_radio_power_cycle(struct ril_radio *self) +{ + if (G_LIKELY(self)) { + struct ril_radio_priv *priv = self->priv; + + if (ril_radio_state_off(priv->last_known_state)) { + DBG("%spower is already off", priv->log_prefix); + GASSERT(!priv->power_cycle); + } else if (priv->power_cycle) { + DBG("%salready in progress", priv->log_prefix); + } else { + DBG("%sinitiated", priv->log_prefix); + priv->power_cycle = TRUE; + if (!priv->pending_id) { + ril_radio_submit_power_request(self, FALSE); + } + } + } +} + +void ril_radio_power_on(struct ril_radio *self, gpointer tag) +{ + if (G_LIKELY(self)) { + struct ril_radio_priv *priv = self->priv; + const gboolean was_on = ril_radio_power_should_be_on(self); + + DBG("%s%p", priv->log_prefix, tag); + g_hash_table_insert(priv->req_table, tag, tag); + if (!was_on) { + ril_radio_power_request(self, TRUE, FALSE); + } + } +} + +void ril_radio_power_off(struct ril_radio *self, gpointer tag) +{ + if (G_LIKELY(self)) { + struct ril_radio_priv *priv = self->priv; + + DBG("%s%p", priv->log_prefix, tag); + if (g_hash_table_remove(priv->req_table, tag) && + !ril_radio_power_should_be_on(self)) { + /* The last one turns the lights off */ + ril_radio_power_request(self, FALSE, FALSE); + } + } +} + +gulong ril_radio_add_state_changed_handler(struct ril_radio *self, + ril_radio_cb_t cb, void *arg) +{ + return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self, + SIGNAL_STATE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0; +} + +void ril_radio_remove_handler(struct ril_radio *self, gulong id) +{ + if (G_LIKELY(self) && G_LIKELY(id)) { + g_signal_handler_disconnect(self, id); + } +} + +static void ril_radio_state_changed(GRilIoChannel *io, guint code, + const void *data, guint len, void *user_data) +{ + struct ril_radio *self = user_data; + GRilIoParser rilp; + int radio_state; + + GASSERT(code == RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED); + grilio_parser_init(&rilp, data, len); + if (grilio_parser_get_int32(&rilp, &radio_state)) { + struct ril_radio_priv *priv = self->priv; + + DBG("%s%s", priv->log_prefix, + ril_radio_state_to_string(radio_state)); + GASSERT(!priv->pending_id || !priv->retry_id); + + if (priv->power_cycle && ril_radio_state_off(radio_state)) { + DBG("%sswitched off for power cycle", priv->log_prefix); + priv->power_cycle = FALSE; + } + + if (priv->pending_id) { + priv->state_changed_while_request_pending++; + } + + priv->last_known_state = radio_state; + ril_radio_check_state(self); + } else { + ofono_error("Error parsing radio state"); + } +} + +struct ril_radio *ril_radio_new(GRilIoChannel *io) +{ + struct ril_radio *self = g_object_new(RIL_RADIO_TYPE, NULL); + struct ril_radio_priv *priv = self->priv; + + priv->io = grilio_channel_ref(io); + priv->q = grilio_queue_new(priv->io); + priv->log_prefix = + (io && io->name && io->name[0] && strcmp(io->name, "RIL")) ? + g_strconcat(io->name, " ", NULL) : g_strdup(""); + DBG("%s", priv->log_prefix); + priv->state_event_id = grilio_channel_add_unsol_event_handler(priv->io, + ril_radio_state_changed, + RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, self); + return self; +} + +struct ril_radio *ril_radio_ref(struct ril_radio *self) +{ + if (G_LIKELY(self)) { + g_object_ref(RIL_RADIO(self)); + return self; + } else { + return NULL; + } +} + +void ril_radio_unref(struct ril_radio *self) +{ + if (G_LIKELY(self)) { + g_object_unref(RIL_RADIO(self)); + } +} + +static void ril_radio_init(struct ril_radio *self) +{ + struct ril_radio_priv *priv = G_TYPE_INSTANCE_GET_PRIVATE(self, + RIL_RADIO_TYPE, struct ril_radio_priv); + self->priv = priv; + priv->req_table = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, NULL); +} + +static void ril_radio_dispose(GObject *object) +{ + struct ril_radio *self = RIL_RADIO(object); + struct ril_radio_priv *priv = self->priv; + + if (priv->state_event_id) { + grilio_channel_remove_handler(priv->io, priv->state_event_id); + priv->state_event_id = 0; + } + if (priv->pending_id) { + grilio_queue_cancel_request(priv->q, priv->pending_id, FALSE); + priv->pending_id = 0; + } + priv->next_state_valid = FALSE; + ril_radio_cancel_retry(self); + grilio_queue_cancel_all(priv->q, FALSE); + G_OBJECT_CLASS(ril_radio_parent_class)->dispose(object); +} + +static void ril_radio_finalize(GObject *object) +{ + struct ril_radio *self = RIL_RADIO(object); + struct ril_radio_priv *priv = self->priv; + + DBG("%s", priv->log_prefix); + g_free(priv->log_prefix); + grilio_channel_unref(priv->io); + grilio_queue_unref(priv->q); + g_hash_table_unref(priv->req_table); + G_OBJECT_CLASS(ril_radio_parent_class)->finalize(object); +} + +static void ril_radio_class_init(RilRadioClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = ril_radio_dispose; + object_class->finalize = ril_radio_finalize; + g_type_class_add_private(klass, sizeof(struct ril_radio_priv)); + ril_radio_signals[SIGNAL_STATE_CHANGED] = + g_signal_new(SIGNAL_STATE_CHANGED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/ofono/drivers/ril/ril_radio.h b/ofono/drivers/ril/ril_radio.h new file mode 100644 index 000000000..c8ee6eca9 --- /dev/null +++ b/ofono/drivers/ril/ril_radio.h @@ -0,0 +1,49 @@ +/* + * oFono - Open Source Telephony - RIL-based devices + * + * Copyright (C) 2015-2016 Jolla Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef RIL_RADIO_H +#define RIL_RADIO_H + +#include "ril_types.h" + +struct ril_radio { + GObject object; + struct ril_radio_priv *priv; + enum ril_radio_state state; +}; + +typedef void (*ril_radio_cb_t)(struct ril_radio *radio, void *arg); + +struct ril_radio *ril_radio_new(GRilIoChannel *io); +struct ril_radio *ril_radio_ref(struct ril_radio *radio); +void ril_radio_unref(struct ril_radio *radio); + +void ril_radio_power_on(struct ril_radio *radio, gpointer tag); +void ril_radio_power_off(struct ril_radio *radio, gpointer tag); +void ril_radio_confirm_power_on(struct ril_radio *radio); +void ril_radio_power_cycle(struct ril_radio *radio); +gulong ril_radio_add_state_changed_handler(struct ril_radio *radio, + ril_radio_cb_t cb, void *arg); +void ril_radio_remove_handler(struct ril_radio *radio, gulong id); + +#endif /* RIL_RADIO */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/ofono/drivers/ril/ril_sim.c b/ofono/drivers/ril/ril_sim.c index e31bb3b3f..a5dd947b6 100644 --- a/ofono/drivers/ril/ril_sim.c +++ b/ofono/drivers/ril/ril_sim.c @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,15 +14,16 @@ */ #include "ril_plugin.h" +#include "ril_radio.h" +#include "ril_sim_card.h" #include "ril_util.h" -#include "ril_constants.h" #include "ril_log.h" #include "simutil.h" #include "util.h" #include "ofono.h" -#define SIM_STATUS_RETRY_SECS (2) +#define SIM_STATE_CHANGE_TIMEOUT_SECS (5) /* * TODO: @@ -53,28 +54,6 @@ #define ENTER_SIM_PUK_PARAMS 3 #define CHANGE_SIM_PIN_PARAMS 3 -#define MAX_UICC_APPS 16 - -struct ril_sim_status { - guint card_state; - guint pin_state; - guint gsm_umts_index; - guint cdma_index; - guint ims_index; - guint num_apps; -}; - -struct ril_sim_app { - guint app_type; - guint app_state; - guint perso_substate; - char *aid_str; - char *app_str; - guint pin_replaced; - guint pin1_state; - guint pin2_state; -}; - /* * TODO: CDMA/IMS * @@ -90,21 +69,22 @@ struct ril_sim { GRilIoChannel *io; GRilIoQueue *q; GRilIoQueue *q2; + GList *pin_cbd_list; struct ofono_sim *sim; - guint slot; - gchar *aid_str; - int app_type; - gchar *app_str; - guint app_index; - enum ofono_sim_password_type passwd_type; + struct ril_sim_card *card; + struct ril_radio *radio; + enum ofono_sim_password_type ofono_passwd_state; int retries[OFONO_SIM_PASSWORD_INVALID]; - enum ofono_sim_password_type passwd_state; - gboolean initialized; - gboolean removed; - guint retry_status_timer_id; + guint slot; + gboolean inserted; guint idle_id; - guint status_req_id; - gulong event_id; + gulong card_status_id; + gulong radio_state_id; + + /* query_passwd_state context */ + ofono_sim_passwd_cb_t query_passwd_state_cb; + void *query_passwd_state_cb_data; + guint query_passwd_state_timeout_id; }; struct ril_sim_cbd { @@ -113,16 +93,24 @@ struct ril_sim_cbd { ofono_sim_file_info_cb_t file_info; ofono_sim_read_cb_t read; ofono_sim_imsi_cb_t imsi; - ofono_sim_passwd_cb_t passwd; - ofono_sim_lock_unlock_cb_t lock_unlock; gpointer ptr; } cb; gpointer data; }; -#define ril_sim_cbd_free g_free +struct ril_sim_pin_cbd { + struct ril_sim *sd; + ofono_sim_lock_unlock_cb_t cb; + gpointer data; + struct ril_sim_card *card; + enum ofono_sim_password_type passwd_type; + int ril_status; + guint state_event_count; + guint timeout_id; + gulong card_status_id; +}; -static void ril_sim_request_status(struct ril_sim *sd); +#define ril_sim_cbd_free g_free static inline struct ril_sim *ril_sim_get_data(struct ofono_sim *sim) { @@ -140,30 +128,92 @@ static struct ril_sim_cbd *ril_sim_cbd_new(struct ril_sim *sd, void *cb, return cbd; } +static void ril_sim_pin_cbd_state_event_count_cb(struct ril_sim_card *sc, + void *user_data) +{ + struct ril_sim_pin_cbd *cbd = user_data; + + /* Cound the SIM status events received while request is pending + * so that ril_sim_pin_change_state_cb can decide whether to wait + * for the next event or not */ + cbd->state_event_count++; +} + +static struct ril_sim_pin_cbd *ril_sim_pin_cbd_new(struct ril_sim *sd, + enum ofono_sim_password_type passwd_type, + ofono_sim_lock_unlock_cb_t cb, void *data) +{ + struct ril_sim_pin_cbd *cbd = g_new0(struct ril_sim_pin_cbd, 1); + + cbd->sd = sd; + cbd->cb = cb; + cbd->data = data; + cbd->passwd_type = passwd_type; + cbd->card = ril_sim_card_ref(sd->card); + cbd->card_status_id = ril_sim_card_add_status_received_handler(sd->card, + ril_sim_pin_cbd_state_event_count_cb, cbd); + return cbd; +} + +static void ril_sim_pin_cbd_free(struct ril_sim_pin_cbd *cbd) +{ + DBG("%p", cbd); + if (cbd->timeout_id) { + g_source_remove(cbd->timeout_id); + } + + ril_sim_card_remove_handler(cbd->card, cbd->card_status_id); + ril_sim_card_unref(cbd->card); + g_free(cbd); +} + +static void ril_sim_pin_cbd_list_free_cb(gpointer data) +{ + ril_sim_pin_cbd_free((struct ril_sim_pin_cbd *)data); +} + +static void ril_sim_pin_req_done(gpointer ptr) +{ + struct ril_sim_pin_cbd *cbd = ptr; + + /* Only free if callback isn't waiting for something else to happen */ + if (!cbd->timeout_id) { + GASSERT(!cbd->card_status_id); + ril_sim_pin_cbd_free(cbd); + } +} + +static const char *ril_sim_app_id(struct ril_sim *sd) +{ + return (sd->card && sd->card->app) ? sd->card->app->aid : NULL; +} + int ril_sim_app_type(struct ofono_sim *sim) { struct ril_sim *sd = ril_sim_get_data(sim); - return sd ? sd->app_type : RIL_APPTYPE_UNKNOWN; + return sd ? ril_sim_card_app_type(sd->card) : RIL_APPTYPE_UNKNOWN; } static void ril_sim_append_path(struct ril_sim *sd, GRilIoRequest *req, const int fileid, const guchar *path, const guint path_len) { + const enum ril_app_type app_type = ril_sim_card_app_type(sd->card); guchar db_path[6] = { 0x00 }; char *hex_path = NULL; - int len = 0; + int len; DBG(""); if (path_len > 0 && path_len < 7) { memcpy(db_path, path, path_len); len = path_len; - } else if (sd->app_type == RIL_APPTYPE_USIM) { + } else if (app_type == RIL_APPTYPE_USIM) { len = sim_ef_db_get_path_3g(fileid, db_path); - } else if (sd->app_type == RIL_APPTYPE_SIM) { + } else if (app_type == RIL_APPTYPE_SIM) { len = sim_ef_db_get_path_2g(fileid, db_path); } else { - ofono_error("Unsupported app_type: 0x%x", sd->app_type); + ofono_error("Unsupported app type %d", app_type); + len = 0; } if (len > 0) { @@ -193,7 +243,7 @@ static void ril_sim_append_path(struct ril_sim *sd, GRilIoRequest *req, * be returned. */ - DBG("db_get_path*() returned empty path."); + DBG("returning empty path."); grilio_request_append_utf8(req, NULL); } } @@ -255,8 +305,8 @@ static void ril_sim_file_info_cb(GRilIoChannel *io, int status, * called we must not call the core call back method as otherwise the * core will crash. */ - if (sd->removed == TRUE) { - ofono_error("RIL_CARDSTATE_ABSENT"); + if (!sd->inserted) { + ofono_error("No SIM card"); return; } @@ -317,7 +367,7 @@ static guint ril_sim_request_io(struct ril_sim *sd, GRilIoQueue *q, int fileid, GRilIoRequest *req = grilio_request_sized_new(80); DBG("cmd=0x%.2X,efid=0x%.4X,%d,%d,%d,(null),pin2=(null),aid=%s", - cmd, fileid, p1, p2, p3, sd->aid_str); + cmd, fileid, p1, p2, p3, ril_sim_app_id(sd)); grilio_request_append_int32(req, cmd); grilio_request_append_int32(req, fileid); @@ -327,7 +377,7 @@ static guint ril_sim_request_io(struct ril_sim *sd, GRilIoQueue *q, int fileid, grilio_request_append_int32(req, p3); /* P3 */ grilio_request_append_utf8(req, NULL); /* data; only for writes */ grilio_request_append_utf8(req, NULL); /* pin2; only for writes */ - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); id = grilio_queue_send_request_full(q, req, RIL_REQUEST_SIM_IO, cb, ril_sim_cbd_free, cbd); @@ -516,444 +566,309 @@ static void ril_sim_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb, struct ril_sim *sd = ril_sim_get_data(sim); GRilIoRequest *req = grilio_request_sized_new(60); - DBG("%s", sd->aid_str); + DBG("%s", ril_sim_app_id(sd)); grilio_request_append_int32(req, GET_IMSI_NUM_PARAMS); - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_GET_IMSI, ril_sim_get_imsi_cb, ril_sim_cbd_free, ril_sim_cbd_new(sd, cb, data)); grilio_request_unref(req); } -static void ril_sim_configure_app(struct ril_sim *sd, - struct ril_sim_app **apps, guint index) +static enum ofono_sim_password_type ril_sim_passwd_state(struct ril_sim *sd) { - const struct ril_sim_app *app = apps[index]; - - sd->app_type = app->app_type; - - g_free(sd->aid_str); - sd->aid_str = g_strdup(app->aid_str); - - g_free(sd->app_str); - sd->app_str = g_strdup(app->app_str); - - sd->app_index = index; - - DBG("setting aid_str (AID) to: %s", sd->aid_str); - switch (app->app_state) { - case RIL_APPSTATE_PIN: - sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN; - break; - case RIL_APPSTATE_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK; - break; - case RIL_APPSTATE_SUBSCRIPTION_PERSO: - switch (app->perso_substate) { - case RIL_PERSOSUBSTATE_SIM_NETWORK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PIN; - break; - case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET: - sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PIN; - break; - case RIL_PERSOSUBSTATE_SIM_CORPORATE: - sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PIN; - break; - case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER: - sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PIN; - break; - case RIL_PERSOSUBSTATE_SIM_SIM: - sd->passwd_state = OFONO_SIM_PASSWORD_PHSIM_PIN; - break; - case RIL_PERSOSUBSTATE_SIM_NETWORK_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHNET_PUK; - break; - case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHNETSUB_PUK; - break; - case RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHCORP_PUK; - break; - case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHSP_PUK; - break; - case RIL_PERSOSUBSTATE_SIM_SIM_PUK: - sd->passwd_state = OFONO_SIM_PASSWORD_PHFSIM_PUK; - break; + if (sd->card && sd->card->app) { + const struct ril_sim_card_app *app = sd->card->app; + + switch (app->app_state) { + case RIL_APPSTATE_PIN: + return OFONO_SIM_PASSWORD_SIM_PIN; + case RIL_APPSTATE_PUK: + return OFONO_SIM_PASSWORD_SIM_PUK; + case RIL_APPSTATE_READY: + return OFONO_SIM_PASSWORD_NONE; + case RIL_APPSTATE_SUBSCRIPTION_PERSO: + switch (app->perso_substate) { + case RIL_PERSOSUBSTATE_READY: + return OFONO_SIM_PASSWORD_NONE; + case RIL_PERSOSUBSTATE_SIM_NETWORK: + return OFONO_SIM_PASSWORD_PHNET_PIN; + case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET: + return OFONO_SIM_PASSWORD_PHNETSUB_PIN; + case RIL_PERSOSUBSTATE_SIM_CORPORATE: + return OFONO_SIM_PASSWORD_PHCORP_PIN; + case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER: + return OFONO_SIM_PASSWORD_PHSP_PIN; + case RIL_PERSOSUBSTATE_SIM_SIM: + return OFONO_SIM_PASSWORD_PHSIM_PIN; + case RIL_PERSOSUBSTATE_SIM_NETWORK_PUK: + return OFONO_SIM_PASSWORD_PHNET_PUK; + case RIL_PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK: + return OFONO_SIM_PASSWORD_PHNETSUB_PUK; + case RIL_PERSOSUBSTATE_SIM_CORPORATE_PUK: + return OFONO_SIM_PASSWORD_PHCORP_PUK; + case RIL_PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK: + return OFONO_SIM_PASSWORD_PHSP_PUK; + case RIL_PERSOSUBSTATE_SIM_SIM_PUK: + return OFONO_SIM_PASSWORD_PHFSIM_PUK; + default: + break; + } default: - sd->passwd_state = OFONO_SIM_PASSWORD_NONE; break; - }; - break; - case RIL_APPSTATE_READY: - sd->passwd_state = OFONO_SIM_PASSWORD_NONE; - break; - case RIL_APPSTATE_UNKNOWN: - case RIL_APPSTATE_DETECTED: - default: - sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; - break; + } } + return OFONO_SIM_PASSWORD_INVALID; } -static void ril_sim_set_uicc_subscription(struct ril_sim *sd, - int app_index, int sub_status) +static gboolean ril_sim_app_in_transient_state(struct ril_sim *sd) { - GRilIoRequest *req = grilio_request_sized_new(16); - const guint sub_id = sd->slot; - - DBG("%d,%d,%d,%d", sd->slot, app_index, sub_id, sub_status); - grilio_request_append_int32(req, sd->slot); - grilio_request_append_int32(req, app_index); - grilio_request_append_int32(req, sub_id); - grilio_request_append_int32(req, sub_status); - grilio_queue_send_request(sd->q, req, - RIL_REQUEST_SET_UICC_SUBSCRIPTION); - grilio_request_unref(req); + if (sd->card && sd->card->app) { + const struct ril_sim_card_app *app = sd->card->app; + + switch (app->app_state) { + case RIL_APPSTATE_DETECTED: + return TRUE; + case RIL_APPSTATE_SUBSCRIPTION_PERSO: + switch (app->perso_substate) { + case RIL_PERSOSUBSTATE_UNKNOWN: + case RIL_PERSOSUBSTATE_IN_PROGRESS: + return TRUE; + default: + break; + } + default: + break; + } + } + return FALSE; } -static int ril_sim_select_uicc_subscription(struct ril_sim *sim, - struct ril_sim_app **apps, guint num_apps) +static void ril_sim_insert_check(struct ril_sim *sd) { - int selected_app = -1; - guint i; - - for (i = 0; i < num_apps; i++) { - const int type = apps[i]->app_type; - if (type == RIL_APPTYPE_USIM || type == RIL_APPTYPE_RUIM) { - selected_app = i; + if (!sd->inserted && + ril_sim_passwd_state(sd) != OFONO_SIM_PASSWORD_INVALID) { + switch (sd->radio->state) { + case RADIO_STATE_SIM_READY: + case RADIO_STATE_RUIM_READY: + case RADIO_STATE_ON: + sd->inserted = TRUE; + ofono_info("SIM card OK"); + ofono_sim_inserted_notify(sd->sim, TRUE); + break; + default: break; - } else if (type != RIL_APPTYPE_UNKNOWN && selected_app == -1) { - selected_app = i; } } - - DBG("Select app %d for subscription.", selected_app); - if (selected_app != -1) { - /* Number 1 means activate that app */ - ril_sim_set_uicc_subscription(sim, selected_app, 1); - } - - return selected_app; } -static gboolean ril_sim_parse_status_response(const void *data, guint len, - struct ril_sim_status *status, struct ril_sim_app **apps) +static void ril_sim_finish_passwd_state_query(struct ril_sim *sd, + enum ofono_sim_password_type state) { - GRilIoParser rilp; - int i; - - grilio_parser_init(&rilp, data, len); - - /* - * FIXME: Need to come up with a common scheme for verifying the - * size of RIL message and properly reacting to bad messages. - * This could be a runtime assertion, disconnect, drop/ignore - * the message, ... - * - * 20 is the min length of RIL_CardStatus_v6 as the AppState - * array can be 0-length. - */ - if (len < 20) { - ofono_error("SIM_STATUS reply too small: %d bytes", len); - status->card_state = RIL_CARDSTATE_ERROR; - return FALSE; - } - - grilio_parser_get_uint32(&rilp, &status->card_state); - - /* - * NOTE: - * - * The global pin_status is used for multi-application - * UICC cards. For example, there are SIM cards that - * can be used in both GSM and CDMA phones. Instead - * of managed PINs for both applications, a global PIN - * is set instead. It's not clear at this point if - * such SIM cards are supported by ofono or RILD. - */ - grilio_parser_get_uint32(&rilp, &status->pin_state); - grilio_parser_get_uint32(&rilp, &status->gsm_umts_index); - grilio_parser_get_uint32(&rilp, &status->cdma_index); - grilio_parser_get_uint32(&rilp, &status->ims_index); - grilio_parser_get_uint32(&rilp, &status->num_apps); - - DBG("card_state=%d, universal_pin_state=%d, gsm_umts_index=%d, " - "cdma_index=%d, ims_index=%d", status->card_state, - status->pin_state, status->gsm_umts_index, - status->cdma_index, status->ims_index); - - if (status->card_state != RIL_CARDSTATE_PRESENT) { - return FALSE; + if (sd->query_passwd_state_timeout_id) { + g_source_remove(sd->query_passwd_state_timeout_id); + sd->query_passwd_state_timeout_id = 0; } - DBG("sim num_apps: %d", status->num_apps); - if (status->num_apps > MAX_UICC_APPS) { - ofono_error("SIM error; too many apps: %d", status->num_apps); - status->num_apps = MAX_UICC_APPS; - } - - for (i = 0; i < status->num_apps; i++) { - apps[i] = g_try_new0(struct ril_sim_app, 1); - grilio_parser_get_uint32(&rilp, &apps[i]->app_type); - grilio_parser_get_uint32(&rilp, &apps[i]->app_state); + if (sd->query_passwd_state_cb) { + ofono_sim_passwd_cb_t cb = sd->query_passwd_state_cb; + void *data = sd->query_passwd_state_cb_data; + struct ofono_error error; - /* - * Consider RIL_APPSTATE_ILLEGAL also READY. Even if app state - * is RIL_APPSTATE_ILLEGAL (-1), ICC operations must be - * permitted. Network access requests will anyway be rejected - * and ME will be in limited service. - */ - if (apps[i]->app_state == RIL_APPSTATE_ILLEGAL) { - DBG("RIL_APPSTATE_ILLEGAL => RIL_APPSTATE_READY"); - apps[i]->app_state = RIL_APPSTATE_READY; - } + sd->query_passwd_state_cb = NULL; + sd->query_passwd_state_cb_data = NULL; - grilio_parser_get_uint32(&rilp, &apps[i]->perso_substate); + error.error = 0; + error.type = (state == OFONO_SIM_PASSWORD_INVALID) ? + OFONO_ERROR_TYPE_FAILURE : + OFONO_ERROR_TYPE_NO_ERROR; - /* TODO: we need a way to instruct parcel to skip - * a string, without allocating memory... - */ - apps[i]->aid_str = grilio_parser_get_utf8(&rilp); /* app ID */ - apps[i]->app_str = grilio_parser_get_utf8(&rilp); /* label */ - - grilio_parser_get_uint32(&rilp, &apps[i]->pin_replaced); - grilio_parser_get_uint32(&rilp, &apps[i]->pin1_state); - grilio_parser_get_uint32(&rilp, &apps[i]->pin2_state); - - DBG("app[%d]: type=%d, state=%d, perso_substate=%d, " - "aid_ptr=%s, app_label_ptr=%s, pin1_replaced=%d, " - "pin1=%d, pin2=%d", i, apps[i]->app_type, - apps[i]->app_state, apps[i]->perso_substate, - apps[i]->aid_str, apps[i]->app_str, - apps[i]->pin_replaced, apps[i]->pin1_state, - apps[i]->pin2_state); + sd->ofono_passwd_state = state; + cb(&error, state, data); } - - return TRUE; } -static void ril_sim_free_apps(struct ril_sim_app **apps, guint num_apps) +static void ril_sim_invalidate_passwd_state(struct ril_sim *sd) { guint i; - for (i = 0; i < num_apps; i++) { - g_free(apps[i]->aid_str); - g_free(apps[i]->app_str); - g_free(apps[i]); + sd->ofono_passwd_state = OFONO_SIM_PASSWORD_INVALID; + for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { + sd->retries[i] = -1; } -} -static gboolean ril_sim_status_retry(gpointer user_data) -{ - struct ril_sim *sd = user_data; - - DBG("[%u]", sd->slot); - GASSERT(sd->retry_status_timer_id); - sd->retry_status_timer_id = 0; - ril_sim_request_status(sd); - return FALSE; + ril_sim_finish_passwd_state_query(sd, OFONO_SIM_PASSWORD_INVALID); } -static void ril_sim_status_cb(GRilIoChannel *io, int ril_status, - const void *data, guint len, void *user_data) +static void ril_sim_status_cb(struct ril_sim_card *sc, void *user_data) { struct ril_sim *sd = user_data; - struct ril_sim_app *apps[MAX_UICC_APPS]; - struct ril_sim_status status; - DBG("[%u]", sd->slot); - sd->status_req_id = 0; - - if (ril_status != RIL_E_SUCCESS) { - ofono_error("SIM status request failed: %s", - ril_error_to_string(ril_status)); - if (!sd->retry_status_timer_id) { - sd->retry_status_timer_id = - g_timeout_add_seconds(SIM_STATUS_RETRY_SECS, - ril_sim_status_retry, sd); - - } - } else if (ril_sim_parse_status_response(data, len, &status, apps) && - status.num_apps) { - - int app_index = status.gsm_umts_index; - - if (app_index < 0) { - app_index = ril_sim_select_uicc_subscription(sd, - apps, status.num_apps); - } - if (app_index >= 0 && app_index < (int)status.num_apps && - apps[app_index]->app_type != RIL_APPTYPE_UNKNOWN) { - ril_sim_configure_app(sd, apps, app_index); - } - - sd->removed = FALSE; + GASSERT(sd->card == sc); + if (sc->status && sc->status->card_state == RIL_CARDSTATE_PRESENT) { + if (sc->app) { + enum ofono_sim_password_type ps; - if (sd->passwd_state != OFONO_SIM_PASSWORD_INVALID) { - /* - * ril_sim_parse_status_response returns true only when - * card status is RIL_CARDSTATE_PRESENT, - * ofono_sim_inserted_notify returns if status doesn't - * change. So can notify core always in this branch. - */ - ofono_sim_inserted_notify(sd->sim, TRUE); - - /* TODO: There doesn't seem to be any other - * way to force the core SIM code to - * recheck the PIN. - * Wouldn't __ofono_sim_refresh be - * more appropriate call here?? - * __ofono_sim_refresh(sim, NULL, TRUE, TRUE); - */ - __ofono_sim_recheck_pin(sd->sim); + ril_sim_insert_check(sd); + ps = ril_sim_passwd_state(sd); + if (ps != OFONO_SIM_PASSWORD_INVALID) { + ril_sim_finish_passwd_state_query(sd, ps); + } + } else { + ril_sim_invalidate_passwd_state(sd); } - - ril_sim_free_apps(apps, status.num_apps); - } else if (status.card_state == RIL_CARDSTATE_ABSENT) { - guint i; - ofono_info("RIL_CARDSTATE_ABSENT"); - - sd->passwd_state = OFONO_SIM_PASSWORD_INVALID; - for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { - sd->retries[i] = -1; + } else { + ril_sim_invalidate_passwd_state(sd); + if (sd->inserted) { + sd->inserted = FALSE; + ofono_info("No SIM card"); + ofono_sim_inserted_notify(sd->sim, FALSE); } - - sd->removed = TRUE; - sd->initialized = FALSE; - - ofono_sim_inserted_notify(sd->sim, FALSE); - } -} - -static void ril_sim_request_status(struct ril_sim *sd) -{ - if (!sd->status_req_id) { - sd->status_req_id = grilio_queue_send_request_full(sd->q, - NULL, RIL_REQUEST_GET_SIM_STATUS, - ril_sim_status_cb, NULL, sd); } } -static void ril_sim_status_changed(GRilIoChannel *io, guint code, - const void *data, guint len, void *user_data) +static void ril_sim_radio_state_cb(struct ril_radio *radio, void *user_data) { struct ril_sim *sd = user_data; - GASSERT(code == RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED); - ril_sim_request_status(sd); + ril_sim_insert_check(sd); } static void ril_sim_query_pin_retries(struct ofono_sim *sim, - ofono_sim_pin_retries_cb_t cb, - void *data) + ofono_sim_pin_retries_cb_t cb, void *data) { struct ril_sim *sd = ril_sim_get_data(sim); struct ofono_error error; + DBG("%d", sd->ofono_passwd_state == OFONO_SIM_PASSWORD_INVALID ? -1 : + sd->retries[sd->ofono_passwd_state]); cb(ril_error_ok(&error), sd->retries, data); } -static void ril_sim_query_passwd_state_cb(GRilIoChannel *io, int err, - const void *data, guint len, void *user_data) +static gboolean ril_sim_query_passwd_state_timeout_cb(gpointer user_data) { - struct ril_sim_cbd *cbd = user_data; - struct ril_sim *sd = cbd->sd; - ofono_sim_passwd_cb_t cb = cbd->cb.passwd; - struct ril_sim_app *apps[MAX_UICC_APPS]; - struct ril_sim_status status; - const gint state = ofono_sim_get_state(sd->sim); - - if (ril_sim_parse_status_response(data, len, &status, apps) && - status.num_apps) { - const int app_index = status.gsm_umts_index; - - if (app_index >= 0 && app_index < (int)status.num_apps && - apps[app_index]->app_type != RIL_APPTYPE_UNKNOWN) { - ril_sim_configure_app(sd, apps, app_index); - } + struct ril_sim *sd = user_data; - ril_sim_free_apps(apps, status.num_apps); - } + GASSERT(sd->query_passwd_state_cb); + sd->query_passwd_state_timeout_id = 0; + ril_sim_finish_passwd_state_query(sd, OFONO_SIM_PASSWORD_INVALID); + return FALSE; +} - DBG("passwd_state %u", sd->passwd_state); +static void ril_sim_query_passwd_state(struct ofono_sim *sim, + ofono_sim_passwd_cb_t cb, void *data) +{ + struct ril_sim *sd = ril_sim_get_data(sim); + enum ofono_sim_password_type passwd_state = ril_sim_passwd_state(sd); + struct ofono_error error; - /* if pin code required cannot be initialized yet*/ - if (sd->passwd_state == OFONO_SIM_PASSWORD_SIM_PIN) { - sd->initialized = FALSE; + if (sd->query_passwd_state_timeout_id) { + g_source_remove(sd->query_passwd_state_timeout_id); + sd->query_passwd_state_timeout_id = 0; } - /* - * To prevent double call to sim_initialize_after_pin from - * sim_pin_query_cb we must prevent calling sim_pin_query_cb - * when !OFONO_SIM_STATE_READY && OFONO_SIM_PASSWORD_NONE - */ - if ((state == OFONO_SIM_STATE_READY) || (sd->initialized == FALSE) || - (sd->passwd_state != OFONO_SIM_PASSWORD_NONE)){ - struct ofono_error error; - - if (sd->passwd_state == OFONO_SIM_PASSWORD_NONE) { - sd->initialized = TRUE; - } + if (passwd_state != OFONO_SIM_PASSWORD_INVALID) { + DBG("%d", passwd_state); + sd->query_passwd_state_cb = NULL; + sd->query_passwd_state_cb_data = NULL; + sd->ofono_passwd_state = passwd_state; + cb(ril_error_ok(&error), passwd_state, data); + } else { + /* Wait for the state to change */ + DBG("waiting for the SIM state to change"); + sd->query_passwd_state_cb = cb; + sd->query_passwd_state_cb_data = data; + sd->query_passwd_state_timeout_id = + g_timeout_add_seconds(SIM_STATE_CHANGE_TIMEOUT_SECS, + ril_sim_query_passwd_state_timeout_cb, sd); + } +} - if (state == OFONO_SIM_STATE_LOCKED_OUT) { - sd->initialized = FALSE; - } +static gboolean ril_sim_pin_change_state_timeout_cb(gpointer user_data) +{ + struct ril_sim_pin_cbd *cbd = user_data; + struct ril_sim *sd = cbd->sd; + struct ofono_error error; - if (sd->passwd_state == OFONO_SIM_PASSWORD_INVALID) { - cb(ril_error_failure(&error), -1, cbd->data); - } else { - cb(ril_error_ok(&error), sd->passwd_state, cbd->data); - } - } + DBG("oops..."); + cbd->timeout_id = 0; + sd->pin_cbd_list = g_list_remove(sd->pin_cbd_list, cbd); + cbd->cb(ril_error_failure(&error), cbd->data); + ril_sim_pin_cbd_free(cbd); + return FALSE; } -static void ril_sim_query_passwd_state(struct ofono_sim *sim, - ofono_sim_passwd_cb_t cb, void *data) +static void ril_sim_pin_change_state_status_cb(struct ril_sim_card *sc, + void *user_data) { - struct ril_sim *sd = ril_sim_get_data(sim); + struct ril_sim_pin_cbd *cbd = user_data; + struct ril_sim *sd = cbd->sd; - grilio_queue_send_request_full(sd->q, NULL, - RIL_REQUEST_GET_SIM_STATUS, ril_sim_query_passwd_state_cb, - ril_sim_cbd_free, ril_sim_cbd_new(sd, cb, data)); + if (!ril_sim_app_in_transient_state(sd)) { + struct ofono_error error; + enum ofono_sim_password_type ps = ril_sim_passwd_state(sd); + + if (ps == OFONO_SIM_PASSWORD_INVALID || + cbd->ril_status != RIL_E_SUCCESS) { + DBG("failure"); + cbd->cb(ril_error_failure(&error), cbd->data); + } else { + DBG("success, passwd_state=%d", ps); + cbd->cb(ril_error_ok(&error), cbd->data); + } + + sd->pin_cbd_list = g_list_remove(sd->pin_cbd_list, cbd); + ril_sim_pin_cbd_free(cbd); + } else { + DBG("will keep waiting"); + } } -static void ril_sim_pin_change_state_cb(GRilIoChannel *io, int status, +static void ril_sim_pin_change_state_cb(GRilIoChannel *io, int ril_status, const void *data, guint len, void *user_data) { - struct ril_sim_cbd *cbd = user_data; - ofono_sim_lock_unlock_cb_t cb = cbd->cb.lock_unlock; + struct ril_sim_pin_cbd *cbd = user_data; struct ril_sim *sd = cbd->sd; - struct ofono_error error; GRilIoParser rilp; - int retry_count = 0; - int passwd_type = sd->passwd_type; - int i; - - /* There is no reason to ask SIM status until - * unsolicited sim status change indication - * Looks like state does not change before that. - */ + int retry_count = -1; grilio_parser_init(&rilp, data, len); grilio_parser_get_int32(&rilp, NULL); grilio_parser_get_int32(&rilp, &retry_count); - for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { - sd->retries[i] = -1; - } - - sd->retries[passwd_type] = retry_count; - + sd->retries[cbd->passwd_type] = retry_count; DBG("result=%d passwd_type=%d retry_count=%d", - status, passwd_type, retry_count); - - error.error = 0; - error.type = (status == RIL_E_SUCCESS) ? - OFONO_ERROR_TYPE_NO_ERROR : - OFONO_ERROR_TYPE_FAILURE; + ril_status, cbd->passwd_type, retry_count); + + cbd->ril_status = ril_status; + if (!cbd->state_event_count || ril_sim_app_in_transient_state(sd)) { + + GASSERT(!g_list_find(sd->pin_cbd_list, cbd)); + GASSERT(!cbd->timeout_id); + + /* Wait for rild to change the state */ + DBG("waiting for SIM state change"); + sd->pin_cbd_list = g_list_append(sd->pin_cbd_list, cbd); + cbd->timeout_id = + g_timeout_add_seconds(SIM_STATE_CHANGE_TIMEOUT_SECS, + ril_sim_pin_change_state_timeout_cb, cbd); + + /* We no longer need to maintain state_event_count, + * replace the SIM state event handler */ + ril_sim_card_remove_handler(cbd->card, cbd->card_status_id); + cbd->card_status_id = + ril_sim_card_add_status_received_handler(sd->card, + ril_sim_pin_change_state_status_cb, cbd); + } else { + struct ofono_error error; - cb(&error, cbd->data); + /* Looks like the state has already changed */ + if (ril_status == RIL_E_SUCCESS) { + cbd->cb(ril_error_ok(&error), cbd->data); + } else { + cbd->cb(ril_error_failure(&error), cbd->data); + } + } } static void ril_sim_pin_send(struct ofono_sim *sim, const char *passwd, @@ -962,16 +877,14 @@ static void ril_sim_pin_send(struct ofono_sim *sim, const char *passwd, struct ril_sim *sd = ril_sim_get_data(sim); GRilIoRequest *req = grilio_request_sized_new(60); - /* Should passwd_type be stored in cbd? */ - sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PIN; grilio_request_append_int32(req, ENTER_SIM_PIN_PARAMS); grilio_request_append_utf8(req, passwd); - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); - DBG("%s,aid=%s", passwd, sd->aid_str); - grilio_queue_send_request_full(sd->q, req, - RIL_REQUEST_ENTER_SIM_PIN, ril_sim_pin_change_state_cb, - ril_sim_cbd_free, ril_sim_cbd_new(sd, cb, data)); + DBG("%s,aid=%s", passwd, ril_sim_app_id(sd)); + grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_ENTER_SIM_PIN, + ril_sim_pin_change_state_cb, ril_sim_pin_req_done, + ril_sim_pin_cbd_new(sd, OFONO_SIM_PASSWORD_SIM_PIN, cb, data)); grilio_request_unref(req); } @@ -1002,10 +915,9 @@ static guint ril_perso_change_state(struct ofono_sim *sim, } if (req) { - sd->passwd_type = passwd_type; id = grilio_queue_send_request_full(sd->q, req, code, - ril_sim_pin_change_state_cb, ril_sim_cbd_free, - ril_sim_cbd_new(sd, cb, data)); + ril_sim_pin_change_state_cb, ril_sim_pin_req_done, + ril_sim_pin_cbd_new(sd, passwd_type, cb, data)); grilio_request_unref(req); } @@ -1051,8 +963,8 @@ static void ril_sim_pin_change_state(struct ofono_sim *sim, break; } - DBG("%d,%s,%d,%s,0,aid=%s", passwd_type, type_str, enable, - passwd, sd->aid_str); + DBG("%d,%s,%d,%s,0,aid=%s", passwd_type, type_str, enable, passwd, + ril_sim_app_id(sd)); if (type_str) { GRilIoRequest *req = grilio_request_sized_new(60); @@ -1062,13 +974,12 @@ static void ril_sim_pin_change_state(struct ofono_sim *sim, RIL_FACILITY_LOCK : RIL_FACILITY_UNLOCK); grilio_request_append_utf8(req, passwd); grilio_request_append_utf8(req, "0"); /* class */ - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); - sd->passwd_type = passwd_type; id = grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_SET_FACILITY_LOCK, - ril_sim_pin_change_state_cb, ril_sim_cbd_free, - ril_sim_cbd_new(sd, cb, data)); + ril_sim_pin_change_state_cb, ril_sim_pin_req_done, + ril_sim_pin_cbd_new(sd, passwd_type, cb, data)); grilio_request_unref(req); } @@ -1087,13 +998,12 @@ static void ril_sim_pin_send_puk(struct ofono_sim *sim, grilio_request_append_int32(req, ENTER_SIM_PUK_PARAMS); grilio_request_append_utf8(req, puk); grilio_request_append_utf8(req, passwd); - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); - DBG("puk=%s,pin=%s,aid=%s", puk, passwd, sd->aid_str); - sd->passwd_type = OFONO_SIM_PASSWORD_SIM_PUK; - grilio_queue_send_request_full(sd->q, req, - RIL_REQUEST_ENTER_SIM_PUK, ril_sim_pin_change_state_cb, - ril_sim_cbd_free, ril_sim_cbd_new(sd, cb, data)); + DBG("puk=%s,pin=%s,aid=%s", puk, passwd, ril_sim_app_id(sd)); + grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_ENTER_SIM_PUK, + ril_sim_pin_change_state_cb, ril_sim_pin_req_done, + ril_sim_pin_cbd_new(sd, OFONO_SIM_PASSWORD_SIM_PUK, cb, data)); grilio_request_unref(req); } @@ -1108,15 +1018,14 @@ static void ril_sim_change_passwd(struct ofono_sim *sim, grilio_request_append_int32(req, CHANGE_SIM_PIN_PARAMS); grilio_request_append_utf8(req, old_passwd); grilio_request_append_utf8(req, new_passwd); - grilio_request_append_utf8(req, sd->aid_str); + grilio_request_append_utf8(req, ril_sim_app_id(sd)); - DBG("old=%s,new=%s,aid=%s", old_passwd, new_passwd, sd->aid_str); - sd->passwd_type = passwd_type; + DBG("old=%s,new=%s,aid=%s", old_passwd, new_passwd, ril_sim_app_id(sd)); grilio_queue_send_request_full(sd->q, req, (passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) ? RIL_REQUEST_CHANGE_SIM_PIN2 : RIL_REQUEST_CHANGE_SIM_PIN, - ril_sim_pin_change_state_cb, ril_sim_cbd_free, - ril_sim_cbd_new(sd, cb, data)); + ril_sim_pin_change_state_cb, ril_sim_pin_req_done, + ril_sim_pin_cbd_new(sd, passwd_type, cb, data)); } static gboolean ril_sim_register(gpointer user) @@ -1127,12 +1036,16 @@ static gboolean ril_sim_register(gpointer user) GASSERT(sd->idle_id); sd->idle_id = 0; - ril_sim_request_status(sd); ofono_sim_register(sd->sim); - sd->event_id = grilio_channel_add_unsol_event_handler(sd->io, - ril_sim_status_changed, RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, - sd); + /* Register for change notifications */ + sd->radio_state_id = ril_radio_add_state_changed_handler(sd->radio, + ril_sim_radio_state_cb, sd); + sd->card_status_id = ril_sim_card_add_status_changed_handler(sd->card, + ril_sim_status_cb, sd); + + /* Check the current state */ + ril_sim_status_cb(sd->card, sd); return FALSE; } @@ -1146,13 +1059,15 @@ static int ril_sim_probe(struct ofono_sim *sim, unsigned int vendor, sd->sim = sim; sd->slot = ril_modem_slot(modem); sd->io = grilio_channel_ref(ril_modem_io(modem)); + sd->card = ril_sim_card_ref(modem->sim_card); + sd->radio = ril_radio_ref(modem->radio); - /* NB: One queue is used for the requests generated by the ofono - * code, and the second one for the requests initiated internally + /* NB: One queue is used for the requests originated from the ofono + * core, and the second one if for the requests initiated internally * by the RIL code. * * The difference is that when SIM card is removed, ofono requests - * are cancelled without invoking they completion callbacks (otherwise + * are cancelled without invoking the completion callbacks (otherwise * ofono would crash) while our completion callbacks have to be * notified in this case (otherwise we would leak memory) */ @@ -1161,6 +1076,7 @@ static int ril_sim_probe(struct ofono_sim *sim, unsigned int vendor, DBG("[%u]", sd->slot); + sd->ofono_passwd_state = OFONO_SIM_PASSWORD_INVALID; for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) { sd->retries[i] = -1; } @@ -1175,6 +1091,7 @@ static void ril_sim_remove(struct ofono_sim *sim) struct ril_sim *sd = ril_sim_get_data(sim); DBG("[%u]", sd->slot); + g_list_free_full(sd->pin_cbd_list, ril_sim_pin_cbd_list_free_cb); grilio_queue_cancel_all(sd->q, FALSE); grilio_queue_cancel_all(sd->q2, TRUE); ofono_sim_set_data(sim, NULL); @@ -1183,16 +1100,19 @@ static void ril_sim_remove(struct ofono_sim *sim) g_source_remove(sd->idle_id); } - if (sd->retry_status_timer_id) { - g_source_remove(sd->retry_status_timer_id); + if (sd->query_passwd_state_timeout_id) { + g_source_remove(sd->query_passwd_state_timeout_id); } - grilio_channel_remove_handler(sd->io, sd->event_id); + ril_radio_remove_handler(sd->radio, sd->radio_state_id); + ril_radio_unref(sd->radio); + + ril_sim_card_remove_handler(sd->card, sd->card_status_id); + ril_sim_card_unref(sd->card); + grilio_channel_unref(sd->io); grilio_queue_unref(sd->q); grilio_queue_unref(sd->q2); - g_free(sd->aid_str); - g_free(sd->app_str); g_free(sd); } diff --git a/ofono/drivers/ril/ril_sim_card.c b/ofono/drivers/ril/ril_sim_card.c new file mode 100644 index 000000000..a44bf6638 --- /dev/null +++ b/ofono/drivers/ril/ril_sim_card.c @@ -0,0 +1,552 @@ +/* + * oFono - Open Source Telephony - RIL-based devices + * + * Copyright (C) 2015-2016 Jolla Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "ril_sim_card.h" +#include "ril_radio.h" +#include "ril_util.h" +#include "ril_log.h" + +#include +#include +#include + +typedef GObjectClass RilSimCardClass; +typedef struct ril_sim_card RilSimCard; + +enum ril_sim_card_event { + EVENT_SIM_STATUS_CHANGED, + EVENT_UICC_SUBSCRIPTION_STATUS_CHANGED, + EVENT_COUNT +}; + +struct ril_sim_card_priv { + GRilIoChannel *io; + GRilIoQueue *q; + guint retry_status_timer_id; + guint status_req_id; + gulong event_id[EVENT_COUNT]; +}; + +enum ril_sim_card_signal { + SIGNAL_STATUS_RECEIVED, + SIGNAL_STATUS_CHANGED, + SIGNAL_STATE_CHANGED, + SIGNAL_APP_CHANGED, + SIGNAL_COUNT +}; + +#define SIGNAL_STATUS_RECEIVED_NAME "ril-simcard-status-received" +#define SIGNAL_STATUS_CHANGED_NAME "ril-simcard-status-changed" +#define SIGNAL_STATE_CHANGED_NAME "ril-simcard-state-changed" +#define SIGNAL_APP_CHANGED_NAME "ril-simcard-app-changed" + +static guint ril_sim_card_signals[SIGNAL_COUNT] = { 0 }; + +G_DEFINE_TYPE(RilSimCard, ril_sim_card, G_TYPE_OBJECT) +#define RIL_SIMCARD_TYPE (ril_sim_card_get_type()) +#define RIL_SIMCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \ + RIL_SIMCARD_TYPE, RilSimCard)) + +#define RIL_SIMCARD_STATE_CHANGED (0x01) +#define RIL_SIMCARD_STATUS_CHANGED (0x02) + +static void ril_sim_card_request_status(struct ril_sim_card *self); + +static gboolean ril_sim_card_app_equal(const struct ril_sim_card_app *a1, + const struct ril_sim_card_app *a2) +{ + if (a1 == a2) { + return TRUE; + } else if (!a1 || !a2) { + return FALSE; + } else { + return a1->app_type == a2->app_type && + a1->app_state == a2->app_state && + a1->perso_substate == a2->perso_substate && + a1->pin_replaced == a2->pin_replaced && + a1->pin1_state == a2->pin1_state && + a1->pin2_state == a2->pin2_state && + !g_strcmp0(a1->aid, a2->aid) && + !g_strcmp0(a1->label, a2->label); + } +} + +static int ril_sim_card_status_compare(const struct ril_sim_card_status *s1, + const struct ril_sim_card_status *s2) +{ + if (s1 == s2) { + return 0; + } else if (!s1 || !s2) { + return RIL_SIMCARD_STATE_CHANGED | RIL_SIMCARD_STATUS_CHANGED; + } else { + int diff = 0; + + if (s1->card_state != s2->card_state) { + diff |= RIL_SIMCARD_STATE_CHANGED; + } + + if (s1->pin_state != s2->pin_state || + s1->gsm_umts_index != s2->gsm_umts_index || + s1->cdma_index != s2->cdma_index || + s1->ims_index != s2->ims_index || + s1->num_apps != s2->num_apps) { + diff |= RIL_SIMCARD_STATUS_CHANGED; + } else { + int i; + + for (i = 0; i < s1->num_apps; i++) { + if (!ril_sim_card_app_equal(s1->apps + i, + s2->apps + i)) { + diff |= RIL_SIMCARD_STATUS_CHANGED; + break; + } + } + } + + return diff; + } +} + +static void ril_sim_card_status_free(struct ril_sim_card_status *status) +{ + if (status) { + if (status->apps) { + int i; + + for (i = 0; i < status->num_apps; i++) { + g_free(status->apps[i].aid); + g_free(status->apps[i].label); + } + g_free(status->apps); + } + g_free(status); + } +} + +static void ril_sim_card_subscribe(struct ril_sim_card *self, + int app_index, int sub_status) +{ + struct ril_sim_card_priv *priv = self->priv; + GRilIoRequest *req = grilio_request_sized_new(16); + const guint sub_id = self->slot; + + DBG("%u,%d,%u,%d", self->slot, app_index, sub_id, sub_status); + grilio_request_append_int32(req, self->slot); + grilio_request_append_int32(req, app_index); + grilio_request_append_int32(req, sub_id); + grilio_request_append_int32(req, sub_status); + grilio_queue_send_request(priv->q, req, + RIL_REQUEST_SET_UICC_SUBSCRIPTION); + grilio_request_unref(req); +} + +static int ril_sim_card_select_app(const struct ril_sim_card_status *status) +{ + int selected_app = -1; + guint i; + + for (i = 0; i < status->num_apps; i++) { + const int type = status->apps[i].app_type; + if (type == RIL_APPTYPE_USIM || type == RIL_APPTYPE_RUIM) { + selected_app = i; + break; + } else if (type != RIL_APPTYPE_UNKNOWN && selected_app == -1) { + selected_app = i; + } + } + + DBG("%d", selected_app); + return selected_app; +} + +static void ril_sim_card_update_app(struct ril_sim_card *self) +{ + const struct ril_sim_card_app *old_app = self->app; + const struct ril_sim_card_status *status = self->status; + int app_index; + + if (status->card_state == RIL_CARDSTATE_PRESENT) { + if (status->gsm_umts_index >= 0 && + status->gsm_umts_index < status->num_apps) { + app_index = status->gsm_umts_index; + } else { + app_index = ril_sim_card_select_app(status); + if (app_index >= 0) { + ril_sim_card_subscribe(self, app_index, 1); + } + } + } else { + app_index = -1; + } + + if (app_index >= 0 && + status->apps[app_index].app_type != RIL_APPTYPE_UNKNOWN) { + self->app = status->apps + app_index; + } else { + self->app = NULL; + } + + if (!ril_sim_card_app_equal(old_app, self->app)) { + g_signal_emit(self, + ril_sim_card_signals[SIGNAL_APP_CHANGED], 0); + } +} + +static void ril_sim_card_update_status(struct ril_sim_card *self, + struct ril_sim_card_status *status) +{ + const int diff = ril_sim_card_status_compare(self->status, status); + + if (diff) { + struct ril_sim_card_status *old_status = self->status; + + self->status = status; + ril_sim_card_update_app(self); + g_signal_emit(self, ril_sim_card_signals[ + SIGNAL_STATUS_RECEIVED], 0); + if (diff & RIL_SIMCARD_STATUS_CHANGED) { + DBG("status changed"); + g_signal_emit(self, ril_sim_card_signals[ + SIGNAL_STATUS_CHANGED], 0); + } + if (diff & RIL_SIMCARD_STATE_CHANGED) { + DBG("state changed"); + g_signal_emit(self, ril_sim_card_signals[ + SIGNAL_STATE_CHANGED], 0); + } + ril_sim_card_status_free(old_status); + } else { + ril_sim_card_status_free(status); + g_signal_emit(self, ril_sim_card_signals[ + SIGNAL_STATUS_RECEIVED], 0); + } +} + +static gboolean ril_sim_card_app_parse(GRilIoParser *rilp, + struct ril_sim_card_app *app) +{ + gint32 app_type, app_state, perso_substate; + gint32 pin_replaced, pin1_state, pin2_state; + + grilio_parser_get_int32(rilp, &app_type); + grilio_parser_get_int32(rilp, &app_state); + + /* + * Consider RIL_APPSTATE_ILLEGAL also READY. Even if app state is + * RIL_APPSTATE_ILLEGAL (-1), ICC operations must be permitted. + * Network access requests will anyway be rejected and ME will be + * in limited service. + */ + if (app_state == RIL_APPSTATE_ILLEGAL) { + DBG("RIL_APPSTATE_ILLEGAL => RIL_APPSTATE_READY"); + app_state = RIL_APPSTATE_READY; + } + + grilio_parser_get_int32(rilp, &perso_substate); + app->aid = grilio_parser_get_utf8(rilp); + app->label = grilio_parser_get_utf8(rilp); + + if (grilio_parser_get_int32(rilp, &pin_replaced) && + grilio_parser_get_int32(rilp, &pin1_state) && + grilio_parser_get_int32(rilp, &pin2_state)) { + + app->app_type = app_type; + app->app_state = app_state; + app->perso_substate = perso_substate; + app->pin_replaced = pin_replaced; + app->pin1_state = pin1_state; + app->pin2_state = pin2_state; + + return TRUE; + } + + return FALSE; +} + +static struct ril_sim_card_status *ril_sim_card_status_parse(const void *data, + guint len) +{ + GRilIoParser rilp; + gint32 card_state, pin_state, gsm_umts_index, cdma_index; + gint32 ims_index, num_apps; + + grilio_parser_init(&rilp, data, len); + + if (!grilio_parser_get_int32(&rilp, &card_state) || + !grilio_parser_get_int32(&rilp, &pin_state) || + !grilio_parser_get_int32(&rilp, &gsm_umts_index) || + !grilio_parser_get_int32(&rilp, &cdma_index) || + !grilio_parser_get_int32(&rilp, &ims_index) || + !grilio_parser_get_int32(&rilp, &num_apps)) { + ofono_error("Failed to parse SIM card status request"); + return NULL; + } else if (num_apps < 0 || num_apps > RIL_CARD_MAX_APPS) { + ofono_error("Invalid SIM app count %d", num_apps); + return NULL; + } else { + int i; + struct ril_sim_card_status *status = + g_new0(struct ril_sim_card_status, 1); + + DBG("card_state=%d, universal_pin_state=%d, gsm_umts_index=%d, " + "cdma_index=%d, ims_index=%d, num_apps=%d", + card_state, pin_state, gsm_umts_index, cdma_index, + ims_index, num_apps); + + status->card_state = card_state; + status->pin_state = pin_state; + status->gsm_umts_index = gsm_umts_index; + status->cdma_index = cdma_index; + status->ims_index = ims_index; + status->num_apps = num_apps; + + if (num_apps > 0) { + status->apps = g_new0(struct ril_sim_card_app, num_apps); + } + + for (i = 0; i < num_apps; i++) { + struct ril_sim_card_app *app = status->apps + i; + + if (ril_sim_card_app_parse(&rilp, app)) { + DBG("app[%d]: type=%d, state=%d, " + "perso_substate=%d, aid_ptr=%s, " + "label=%s, pin1_replaced=%d, pin1=%d, " + "pin2=%d", i, app->app_type, + app->app_state, app->perso_substate, + app->aid, app->label, + app->pin_replaced, app->pin1_state, + app->pin2_state); + } else { + break; + } + } + + if (i == num_apps) { + return status; + } else { + ril_sim_card_status_free(status); + return NULL; + } + } +} + +static gboolean ril_sim_card_status_retry(gpointer user_data) +{ + struct ril_sim_card *self = user_data; + struct ril_sim_card_priv *priv = self->priv; + + GASSERT(priv->retry_status_timer_id); + priv->retry_status_timer_id = 0; + ril_sim_card_request_status(self); + return FALSE; +} + +static void ril_sim_card_schedule_retry(struct ril_sim_card *self) +{ + struct ril_sim_card_priv *priv = self->priv; + + if (!priv->retry_status_timer_id) { + priv->retry_status_timer_id = + g_timeout_add_seconds(RIL_RETRY_SECS, + ril_sim_card_status_retry, self); + } +} + +static void ril_sim_card_status_cb(GRilIoChannel *io, int ril_status, + const void *data, guint len, void *user_data) +{ + struct ril_sim_card *self = user_data; + struct ril_sim_card_priv *priv = self->priv; + + GASSERT(priv->status_req_id); + priv->status_req_id = 0; + + if (ril_status == RIL_E_SUCCESS) { + struct ril_sim_card_status *status = + ril_sim_card_status_parse(data, len); + + if (status) { + ril_sim_card_update_status(self, status); + } else { + ril_sim_card_schedule_retry(self); + } + } else if (ril_status != GRILIO_STATUS_CANCELLED) { + ofono_error("SIM status request failed: %s", + ril_error_to_string(ril_status)); + ril_sim_card_schedule_retry(self); + } else { + DBG("cancelled"); + } +} + +static void ril_sim_card_request_status(struct ril_sim_card *self) +{ + struct ril_sim_card_priv *priv = self->priv; + + if (priv->retry_status_timer_id) { + g_source_remove(priv->retry_status_timer_id); + priv->retry_status_timer_id = 0; + } + + if (!priv->status_req_id) { + priv->status_req_id = grilio_queue_send_request_full(priv->q, + NULL, RIL_REQUEST_GET_SIM_STATUS, + ril_sim_card_status_cb, NULL, self); + } +} + +static void ril_sim_card_status_changed(GRilIoChannel *io, guint code, + const void *data, guint len, void *user_data) +{ + struct ril_sim_card *self = user_data; + + ril_sim_card_request_status(self); +} + +struct ril_sim_card *ril_sim_card_new(GRilIoChannel *io, guint slot) +{ + struct ril_sim_card *self = g_object_new(RIL_SIMCARD_TYPE, NULL); + struct ril_sim_card_priv *priv = self->priv; + + self->slot = slot; + priv->io = grilio_channel_ref(io); + priv->q = grilio_queue_new(io); + priv->event_id[EVENT_SIM_STATUS_CHANGED] = + grilio_channel_add_unsol_event_handler(priv->io, + ril_sim_card_status_changed, + RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, self); + priv->event_id[EVENT_UICC_SUBSCRIPTION_STATUS_CHANGED] = + grilio_channel_add_unsol_event_handler(priv->io, + ril_sim_card_status_changed, + RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, self); + ril_sim_card_request_status(self); + return self; +} + +struct ril_sim_card *ril_sim_card_ref(struct ril_sim_card *self) +{ + if (G_LIKELY(self)) { + g_object_ref(RIL_SIMCARD(self)); + return self; + } else { + return NULL; + } +} + +void ril_sim_card_unref(struct ril_sim_card *self) +{ + if (G_LIKELY(self)) { + g_object_unref(RIL_SIMCARD(self)); + } +} + +gulong ril_sim_card_add_status_received_handler(struct ril_sim_card *self, + ril_sim_card_cb_t cb, void *arg) +{ + return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self, + SIGNAL_STATUS_RECEIVED_NAME, G_CALLBACK(cb), arg) : 0; +} + +gulong ril_sim_card_add_status_changed_handler(struct ril_sim_card *self, + ril_sim_card_cb_t cb, void *arg) +{ + return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self, + SIGNAL_STATUS_CHANGED_NAME, G_CALLBACK(cb), arg) : 0; +} + +gulong ril_sim_card_add_state_changed_handler(struct ril_sim_card *self, + ril_sim_card_cb_t cb, void *arg) +{ + return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self, + SIGNAL_STATE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0; +} + +gulong ril_sim_card_add_app_changed_handler(struct ril_sim_card *self, + ril_sim_card_cb_t cb, void *arg) +{ + return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self, + SIGNAL_APP_CHANGED_NAME, G_CALLBACK(cb), arg) : 0; +} + +void ril_sim_card_remove_handler(struct ril_sim_card *self, gulong id) +{ + if (G_LIKELY(self) && G_LIKELY(id)) { + g_signal_handler_disconnect(self, id); + } +} + +static void ril_sim_card_init(struct ril_sim_card *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RIL_SIMCARD_TYPE, + struct ril_sim_card_priv); +} + +static void ril_sim_card_dispose(GObject *object) +{ + struct ril_sim_card *self = RIL_SIMCARD(object); + struct ril_sim_card_priv *priv = self->priv; + + grilio_channel_remove_handlers(priv->io, priv->event_id, EVENT_COUNT); + grilio_queue_cancel_all(priv->q, TRUE); + G_OBJECT_CLASS(ril_sim_card_parent_class)->dispose(object); +} + +static void ril_sim_card_finalize(GObject *object) +{ + struct ril_sim_card *self = RIL_SIMCARD(object); + struct ril_sim_card_priv *priv = self->priv; + + if (priv->retry_status_timer_id) { + g_source_remove(priv->retry_status_timer_id); + } + + grilio_channel_unref(priv->io); + grilio_queue_unref(priv->q); + ril_sim_card_status_free(self->status); + G_OBJECT_CLASS(ril_sim_card_parent_class)->finalize(object); +} + +static void ril_sim_card_class_init(RilSimCardClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->dispose = ril_sim_card_dispose; + object_class->finalize = ril_sim_card_finalize; + g_type_class_add_private(klass, sizeof(struct ril_sim_card_priv)); + ril_sim_card_signals[SIGNAL_STATUS_RECEIVED] = + g_signal_new(SIGNAL_STATUS_RECEIVED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + ril_sim_card_signals[SIGNAL_STATUS_CHANGED] = + g_signal_new(SIGNAL_STATUS_CHANGED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + ril_sim_card_signals[SIGNAL_STATE_CHANGED] = + g_signal_new(SIGNAL_STATE_CHANGED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + ril_sim_card_signals[SIGNAL_APP_CHANGED] = + g_signal_new(SIGNAL_APP_CHANGED_NAME, + G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, NULL, G_TYPE_NONE, 0); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/ofono/drivers/ril/ril_sim_card.h b/ofono/drivers/ril/ril_sim_card.h new file mode 100644 index 000000000..17d0e0117 --- /dev/null +++ b/ofono/drivers/ril/ril_sim_card.h @@ -0,0 +1,78 @@ +/* + * oFono - Open Source Telephony - RIL-based devices + * + * Copyright (C) 2015-2016 Jolla Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef RIL_SIM_CARD_H +#define RIL_SIM_CARD_H + +#include "ril_types.h" + +struct ril_sim_card_app { + enum ril_app_type app_type; + enum ril_app_state app_state; + enum ril_perso_substate perso_substate; + char *aid; + char *label; + guint pin_replaced; + enum ril_pin_state pin1_state; + enum ril_pin_state pin2_state; +}; + +struct ril_sim_card_status { + enum ril_card_state card_state; + enum ril_pin_state pin_state; + int gsm_umts_index; + int cdma_index; + int ims_index; + int num_apps; + struct ril_sim_card_app *apps; +}; + +struct ril_sim_card { + GObject object; + struct ril_sim_card_priv *priv; + struct ril_sim_card_status *status; + const struct ril_sim_card_app *app; + guint slot; +}; + +typedef void (*ril_sim_card_cb_t)(struct ril_sim_card *sc, void *arg); + +struct ril_sim_card *ril_sim_card_new(GRilIoChannel *io, guint slot); +struct ril_sim_card *ril_sim_card_ref(struct ril_sim_card *sc); +void ril_sim_card_unref(struct ril_sim_card *sc); +gulong ril_sim_card_add_status_received_handler(struct ril_sim_card *sc, + ril_sim_card_cb_t cb, void *arg); +gulong ril_sim_card_add_status_changed_handler(struct ril_sim_card *sc, + ril_sim_card_cb_t cb, void *arg); +gulong ril_sim_card_add_state_changed_handler(struct ril_sim_card *sc, + ril_sim_card_cb_t cb, void *arg); +gulong ril_sim_card_add_app_changed_handler(struct ril_sim_card *sc, + ril_sim_card_cb_t cb, void *arg); +void ril_sim_card_remove_handler(struct ril_sim_card *sc, gulong id); + +/* Inline wrappers */ +G_INLINE_FUNC enum ril_app_type +ril_sim_card_app_type(struct ril_sim_card *sc) + { return (sc && sc->app) ? sc->app->app_type : RIL_APPTYPE_UNKNOWN; } + +#endif /* RIL_SIM_CARD_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 8 + * indent-tabs-mode: t + * End: + */ diff --git a/ofono/drivers/ril/ril_sim_dbus.c b/ofono/drivers/ril/ril_sim_dbus.c index f7d53fe63..076e90217 100644 --- a/ofono/drivers/ril/ril_sim_dbus.c +++ b/ofono/drivers/ril/ril_sim_dbus.c @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,7 +16,6 @@ #include "ril_plugin.h" #include "ril_log.h" -#include #include #include @@ -174,7 +173,7 @@ struct ril_sim_dbus *ril_sim_dbus_new(struct ril_modem *md) if (imsi) { GError *error = NULL; - const struct ril_modem_config *config= ril_modem_config(md); + const struct ril_slot_config *config = &md->config; struct ril_sim_dbus *dbus = g_new0(struct ril_sim_dbus, 1); DBG("%s", ril_modem_get_path(md)); @@ -204,7 +203,7 @@ struct ril_sim_dbus *ril_sim_dbus_new(struct ril_modem *md) if (g_dbus_register_interface(dbus->conn, dbus->path, RIL_SIM_DBUS_INTERFACE, ril_sim_dbus_methods, ril_sim_dbus_signals, NULL, dbus, NULL)) { - ofono_modem_add_interface(ril_modem_ofono_modem(md), + ofono_modem_add_interface(md->ofono, RIL_SIM_DBUS_INTERFACE); return dbus; } else { @@ -222,7 +221,7 @@ void ril_sim_dbus_free(struct ril_sim_dbus *dbus) DBG("%s", dbus->path); g_dbus_unregister_interface(dbus->conn, dbus->path, RIL_SIM_DBUS_INTERFACE); - ofono_modem_remove_interface(ril_modem_ofono_modem(dbus->md), + ofono_modem_remove_interface(dbus->md->ofono, RIL_SIM_DBUS_INTERFACE); dbus_connection_unref(dbus->conn); g_key_file_free(dbus->storage); diff --git a/ofono/drivers/ril/ril_types.h b/ofono/drivers/ril/ril_types.h index 4e40c4814..7aa2ff5cc 100644 --- a/ofono/drivers/ril/ril_types.h +++ b/ofono/drivers/ril/ril_types.h @@ -1,7 +1,7 @@ /* * oFono - Open Source Telephony - RIL-based devices * - * Copyright (C) 2015 Jolla Ltd. + * Copyright (C) 2015-2016 Jolla Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -30,6 +30,15 @@ #include #include +#include "ril_constants.h" + +#define RIL_RETRY_SECS (2) + +struct ril_modem; +struct ril_radio; +struct ril_sim_card; +struct ril_plugin_dbus; + #endif /* RIL_TYPES_H */ /* diff --git a/rpm/ofono.spec b/rpm/ofono.spec index 653a7ae57..92707fc4b 100644 --- a/rpm/ofono.spec +++ b/rpm/ofono.spec @@ -20,7 +20,7 @@ BuildRequires: pkgconfig(bluez) >= 4.85 BuildRequires: pkgconfig(mobile-broadband-provider-info) BuildRequires: pkgconfig(libwspcodec) >= 2.0 BuildRequires: pkgconfig(libglibutil) -BuildRequires: pkgconfig(libgrilio) +BuildRequires: pkgconfig(libgrilio) >= 1.0.3 BuildRequires: libtool BuildRequires: automake BuildRequires: autoconf