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