From 2e0c92ad8cfdd081f8383151003108ddf3ebfad6 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Sun, 8 Sep 2024 17:50:06 +0200 Subject: [PATCH] uqmid: move usim related function into own fsm Signed-off-by: Alexander Couzens --- uqmid/CMakeLists.txt | 2 +- uqmid/modem.c | 3 + uqmid/modem.h | 8 +- uqmid/modem_fsm.c | 361 ++++++---------------------- uqmid/modem_fsm.h | 19 +- uqmid/modem_tx.c | 34 +++ uqmid/modem_tx.h | 2 + uqmid/sim_fsm.c | 551 +++++++++++++++++++++++++++++++++++++++++++ uqmid/sim_fsm.h | 54 +++++ uqmid/ubus.c | 2 + 10 files changed, 724 insertions(+), 312 deletions(-) create mode 100644 uqmid/sim_fsm.c create mode 100644 uqmid/sim_fsm.h diff --git a/uqmid/CMakeLists.txt b/uqmid/CMakeLists.txt index b2e672e..23e8d06 100644 --- a/uqmid/CMakeLists.txt +++ b/uqmid/CMakeLists.txt @@ -1,7 +1,7 @@ SET(UQMID_LIBS ${talloc_library} ${ubus_library}) -SET(UQMID uqmid.c ddev.c ubus.c modem.c modem_fsm.c modem_tx.c services.c sim.c ctrl.c wwan.c gsmtap_util.c) +SET(UQMID uqmid.c ddev.c ubus.c modem.c modem_fsm.c modem_tx.c services.c sim.c sim_fsm.c ctrl.c wwan.c gsmtap_util.c) ADD_SUBDIRECTORY(osmocom) ADD_EXECUTABLE(uqmid ${UQMID}) diff --git a/uqmid/modem.c b/uqmid/modem.c index 71927b8..d935723 100644 --- a/uqmid/modem.c +++ b/uqmid/modem.c @@ -33,6 +33,7 @@ #include "qmi-enums-dms.h" #include "qmi-enums-wds.h" #include "qmi-enums.h" +#include "sim_fsm.h" #include "uqmid.h" #include "qmi-errors.h" #include "qmi-errors.c" @@ -116,8 +117,10 @@ uqmid_modem_add(const char *name, const char *device, bool qmi_over_mbim) modem->qmi->error_cb = modem_error_cb; modem->qmi->error_cb_data = modem; + modem->sim.use_uim = true; modem->fi = modem_fsm_alloc(modem); + modem->sim.fi = sim_fsm_alloc(modem); list_add(&modem->list, &uqmid_modems); modem_fsm_start(modem); diff --git a/uqmid/modem.h b/uqmid/modem.h index 144f961..78cc7fe 100644 --- a/uqmid/modem.h +++ b/uqmid/modem.h @@ -70,8 +70,8 @@ struct modem { char *rev; /* unsure if iccid should be here */ - char *iccid; - char *imsi; + char *iccid; /* FIXME: iccid length is? */ + char imsi[16]; struct modem_config config; struct { @@ -110,11 +110,14 @@ struct modem { } brearer; struct { + /*! sim_fsm instance */ + struct osmo_fsm_inst *fi; /* Do we found a valid simcard */ bool init; /* Certain modems (and maybe simcards) support either unlocking the sim via pin1 or upin. */ enum uqmi_sim_state state; bool use_upin; + bool use_uim; bool requires_unlock; int pin_retries; int puk_retries; @@ -125,6 +128,7 @@ struct modem { struct list_head list; struct ubus_object ubus; + /*! modem_fsm instance */ struct osmo_fsm_inst *fi; }; diff --git a/uqmid/modem_fsm.c b/uqmid/modem_fsm.c index b85f538..045f82c 100644 --- a/uqmid/modem_fsm.c +++ b/uqmid/modem_fsm.c @@ -4,6 +4,7 @@ #include "qmi-enums-dms.h" #include "qmi-errors.h" +#include "sim_fsm.h" #include "talloc.h" #include "qmi-struct.h" @@ -39,43 +40,36 @@ enum { }; static const struct value_string modem_event_names[] = { - { MODEM_EV_REQ_START, "REQ_START" }, - { MODEM_EV_REQ_DESTROY, "REQ_DESTROY" }, - { MODEM_EV_RX_SYNC, "RX_SYNC" }, - { MODEM_EV_RX_VERSION, "RX_VERSION" }, - { MODEM_EV_RX_MODEL, "RX_MODEL" }, - { MODEM_EV_RX_MANUFACTURER, "RX_MANUFACTURER" }, - { MODEM_EV_RX_REVISION, "RX_REVISION" }, - - { MODEM_EV_RX_IMSI, "RX_IMSI" }, - { MODEM_EV_RX_UIM_FAILED, "RX_UIM_FAILED" }, - { MODEM_EV_RX_UIM_GET_SLOT_FAILED, "RX_UIM_GET_SLOT_FAILED" }, - { MODEM_EV_RX_UIM_VALID_ICCID, "RX_UIM_VALID_ICCID" }, - { MODEM_EV_RX_UIM_NO_UIM_FOUND, "RX_UIM_NO_UIM_FOUND" }, - { MODEM_EV_RX_IMSI_DMS_FAILED, "RX_IMSI_DMS_FAILED" }, - - { MODEM_EV_RX_POWEROFF, "RX_POWEROFF" }, - { MODEM_EV_RX_POWERON, "RX_POWERON" }, - { MODEM_EV_RX_POWERSET, "RX_POWERSET" }, - - { MODEM_EV_RX_UNLOCKED_PIN, "RX_UNLOCKED_PIN" }, - { MODEM_EV_RX_UIM_PUK_REQUIRED, "RX_UIM_PUK_REQUIRED" }, - { MODEM_EV_RX_UIM_PIN_REQUIRED, "RX_UIM_PIN_REQUIRED" }, - { MODEM_EV_RX_UIM_READY, "RX_UIM_READY" }, - - { MODEM_EV_RX_GET_PROFILE_LIST, "RX_GET_PROFILE_LIST" }, - { MODEM_EV_RX_MODIFIED_PROFILE, "RX_MODIFIED_PROFILE" }, - { MODEM_EV_RX_CONFIGURED, "RX_CONFIGURED" }, - - { MODEM_EV_RX_SEARCHING, "RX_SEARCHING" }, - { MODEM_EV_RX_UNREGISTERED, "RX_UNREGISTERED" }, - { MODEM_EV_RX_REGISTERED, "RX_REGISTERED" }, - - { MODEM_EV_RX_SUBSCRIBED, "RX_SUBSCRIBED" }, - { MODEM_EV_RX_SUBSCRIBE_FAILED, "RX_SUBSCRIBE_FAILED" }, - - { MODEM_EV_RX_FAILED, "RX_FAILED" }, - { MODEM_EV_RX_SUCCEED, "RX_SUCCEED" }, + { MODEM_EV_REQ_START, "REQ_START" }, + { MODEM_EV_REQ_DESTROY, "REQ_DESTROY" }, + { MODEM_EV_REQ_CONFIGURED, "REQ_CONFIGURED"}, + { MODEM_EV_RX_SYNC, "RX_SYNC" }, + { MODEM_EV_RX_VERSION, "RX_VERSION" }, + { MODEM_EV_RX_MODEL, "RX_MODEL" }, + { MODEM_EV_RX_MANUFACTURER, "RX_MANUFACTURER" }, + { MODEM_EV_RX_REVISION, "RX_REVISION" }, + + { MODEM_EV_RX_POWEROFF, "RX_POWEROFF" }, + { MODEM_EV_RX_POWERON, "RX_POWERON" }, + { MODEM_EV_RX_POWERSET, "RX_POWERSET" }, + + { MODEM_EV_REQ_SIM_TERM, "RX_SIM_TERM" }, + { MODEM_EV_REQ_SIM_FAILURE, "RX_SIM_FAILURE" }, + { MODEM_EV_REQ_SIM_READY, "RX_SIM_READY" }, + + { MODEM_EV_RX_GET_PROFILE_LIST, "RX_GET_PROFILE_LIST" }, + { MODEM_EV_RX_MODIFIED_PROFILE, "RX_MODIFIED_PROFILE" }, + { MODEM_EV_RX_CONFIGURED, "RX_CONFIGURED" }, + + { MODEM_EV_RX_SEARCHING, "RX_SEARCHING" }, + { MODEM_EV_RX_UNREGISTERED, "RX_UNREGISTERED" }, + { MODEM_EV_RX_REGISTERED, "RX_REGISTERED" }, + + { MODEM_EV_RX_SUBSCRIBED, "RX_SUBSCRIBED" }, + { MODEM_EV_RX_SUBSCRIBE_FAILED, "RX_SUBSCRIBE_FAILED" }, + + { MODEM_EV_RX_FAILED, "RX_FAILED" }, + { MODEM_EV_RX_SUCCEED, "RX_SUCCEED" }, { 0, NULL } }; @@ -177,8 +171,11 @@ static void modem_st_get_version_onenter(struct osmo_fsm_inst *fi, uint32_t old_ static void modem_st_get_version(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + struct modem *modem = fi->priv; + switch (event) { case MODEM_EV_RX_VERSION: + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_REQ_START, NULL); osmo_fsm_inst_state_chg(fi, MODEM_ST_GET_MODEL, 0, 0); break; } @@ -304,231 +301,7 @@ static void modem_st_get_model(struct osmo_fsm_inst *fi, uint32_t event, void *d uqmi_service_send_simple(service, qmi_set_dms_get_revision_request, get_revision_cb, modem); break; case MODEM_EV_RX_REVISION: - osmo_fsm_inst_state_chg(fi, MODEM_ST_GET_IMSI, 3, 0); - break; - } -} - -static void uim_get_slot_status_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) -{ - struct modem *modem = req->cb_data; - int ret = 0, i = 0; - char *iccid_str; - - if (req->ret) { - modem_log(modem, LOGL_INFO, "Failed to get slot status. Returned %d/%s. Trying next card status", - req->ret, qmi_get_error_str(req->ret)); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_GET_SLOT_FAILED, NULL); - return; - } - - struct qmi_uim_get_slot_status_response res = {}; - ret = qmi_parse_uim_get_slot_status_response(msg, &res); - if (ret) { - modem_log(modem, LOGL_INFO, "Failed to get slot status."); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_FAILED, NULL); - return; - } - - // res->data.physical_slot_status_n - // struct { - // uint32_t physical_card_status; - // uint32_t physical_slot_status; - // uint8_t logical_slot; - // unsigned int iccid_n; - // uint8_t *iccid; - // } *physical_slot_status; - - for (i = 0; i < res.data.physical_slot_status_n; ++i) { - uint32_t physical_card_status = res.data.physical_slot_status[i].physical_card_status; - uint32_t physical_slot_status = res.data.physical_slot_status[i].physical_slot_status; - /* ignoring uint8_t logical_slot */ - unsigned int iccid_n = res.data.physical_slot_status[i].iccid_n; - uint8_t *iccid_bcd = res.data.physical_slot_status[i].iccid; - - if (physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT) - continue; - - if (physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE) - continue; - - if (iccid_n == 0) { - modem_log(modem, LOGL_INFO, "slot %d: Empty ICCID. UIM not yet ready? Or an EUICC?", i); - continue; - } - - /* FIXME: check for uneven number .. */ - int iccid_str_len = iccid_n * 2; - iccid_str = talloc_zero_size(modem, iccid_n * 2); - ret = osmo_bcd2str(iccid_str, iccid_str_len, iccid_bcd, 0, iccid_n, 1); - - if (ret) { - modem_log(modem, LOGL_INFO, "Couldn't decode ICCID of slot %d", i); - talloc_free(iccid_str); - continue; - } - - if (modem->iccid) { - modem_log(modem, LOGL_INFO, "Modem already contains an ICCID. Replacing old entry %s with %s", - modem->iccid, iccid_str); - talloc_free(modem->iccid); - } - modem->iccid = iccid_str; - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_VALID_ICCID, NULL); - } - - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_NO_UIM_FOUND, NULL); -} - -static void uim_get_card_status_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) -{ - struct modem *modem = req->cb_data; - int ret = 0, i = 0, found = 0; - - if (req->ret) { - modem_log(modem, LOGL_INFO, "Failed to get card status %d/%s.", req->ret, qmi_get_error_str(req->ret)); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_FAILED, NULL); - return; - } - - struct qmi_uim_get_card_status_response res = {}; - ret = qmi_parse_uim_get_card_status_response(msg, &res); - if (ret) { - modem_log(modem, LOGL_INFO, "Failed to get card status. Decoder failed."); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_FAILED, NULL); - return; - } - - for (i = 0; i < res.data.card_status.cards_n; ++i) { - if (res.data.card_status.cards[i].card_state != QMI_UIM_CARD_STATE_PRESENT) - continue; - - for (int j = 0; j < res.data.card_status.cards[i].applications_n; ++j) { - /* TODO: We should prioritize the USIM application. If not found, fallback to SIM -> ISIM -> CSIM */ - if (res.data.card_status.cards[i].applications[j].type == QMI_UIM_CARD_APPLICATION_TYPE_UNKNOWN) - continue; - - modem_log(modem, LOGL_INFO, "Found valid card application type %x", - res.data.card_status.cards[i].applications[j].type); - modem->sim.use_upin = res.data.card_status.cards[i].applications[i].upin_replaces_pin1; - - /* check if pin1 or upin is the correct method */ - if (modem->sim.use_upin) { - modem->sim.state = uim_pin_to_uqmi_state(res.data.card_status.cards[i].upin_state); - modem->sim.pin_retries = res.data.card_status.cards[i].upin_retries; - modem->sim.puk_retries = res.data.card_status.cards[i].upuk_retries; - } else { - modem->sim.state = - uim_pin_to_uqmi_state(res.data.card_status.cards[i].applications[i].pin1_state); - modem->sim.pin_retries = res.data.card_status.cards[i].applications[i].pin1_retries; - modem->sim.puk_retries = res.data.card_status.cards[i].applications[i].puk1_retries; - } - - found = 1; - break; /* handle first application only for now */ - } - - if (found) { - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_VALID_ICCID, NULL); - return; - } - } - - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_NO_UIM_FOUND, NULL); -} - -static void dms_get_imsi_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) -{ - struct modem *modem = req->cb_data; - int ret = 0; - - struct qmi_dms_uim_get_imsi_response res = {}; - ret = qmi_parse_dms_uim_get_imsi_response(msg, &res); - - if (ret) { - modem_log(modem, LOGL_INFO, "Failed to get imsi via dms. Failed to parse message"); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI_DMS_FAILED, NULL); - return; - } - - if (!res.data.imsi || strlen(res.data.imsi)) { - modem_log(modem, LOGL_INFO, "Received empty IMSI"); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI_DMS_FAILED, (void *)1); - return; - } - - modem->imsi = talloc_strdup(modem, res.data.imsi); - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI, NULL); -} - -static void tx_get_imsi_req(struct osmo_fsm_inst *fi, uint32_t old_state) -{ - struct modem *modem = fi->priv; - struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); - - /* FIXME: register for indication of removed uims - * - register_physical_slot_status_events - * - Physical Slot Status? - */ - if (uim) { - modem_log(modem, LOGL_INFO, "Trying to query UIM for slot status."); - uqmi_service_send_simple(uim, qmi_set_uim_get_slot_status_request, uim_get_slot_status_cb, modem); - return; - } - - /* Retrieve it via DMS */ - struct qmi_service *dms = uqmi_service_find(modem->qmi, QMI_SERVICE_DMS); - if (dms) { - modem_log(modem, LOGL_INFO, "Trying to query UIM for IMSI."); - uqmi_service_send_simple(dms, qmi_set_dms_uim_get_imsi_request, dms_get_imsi_cb, modem); - return; - } -} - -static void modem_st_get_imsi_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) -{ - fi->N = 0; - tx_get_imsi_req(fi, old_state); -} - -static void modem_st_get_imsi(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct modem *modem = fi->priv; - struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); - struct qmi_service *dms = uqmi_service_find(modem->qmi, QMI_SERVICE_DMS); - - /* TODO: figure out when DMS: Get IMSI is available, same for Get UIM State. - * Are those legacy calls? - */ - - switch (event) { - case MODEM_EV_RX_UIM_VALID_ICCID: - /* FIXME: ICCID must be enough for now. - * It seems only Get Record or Get Transparent file for UIM would be the only - * available solution to retrieve the IMSI. Ignoring IMSI for now. - */ - modem_log(modem, LOGL_INFO, "Found valid & usable USIM."); - osmo_fsm_inst_state_chg(fi, MODEM_ST_POWEROFF, 0, 0); - break; - case MODEM_EV_RX_UIM_GET_SLOT_FAILED: - if (uim) - uqmi_service_send_simple(uim, qmi_set_uim_get_card_status_request, uim_get_card_status_cb, - modem); - else - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_UIM_FAILED, NULL); - break; - case MODEM_EV_RX_UIM_NO_UIM_FOUND: - modem_log(modem, LOGL_INFO, "No valid UIM found. Waiting for timeout to retry."); - break; - case MODEM_EV_RX_UIM_FAILED: - if (dms) - uqmi_service_send_simple(dms, qmi_set_dms_uim_get_imsi_request, dms_get_imsi_cb, modem); - break; - case MODEM_EV_RX_IMSI: - osmo_fsm_inst_state_chg(fi, MODEM_ST_POWEROFF, 0, 0); - break; - case MODEM_EV_RX_IMSI_DMS_FAILED: - modem_log(modem, LOGL_INFO, "DMS failed to retrieve the IMSI. Err %p", data); + osmo_fsm_inst_state_chg(fi, MODEM_ST_POWEROFF, 3, 0); break; } } @@ -609,22 +382,33 @@ static void modem_st_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *da break; case MODEM_EV_RX_POWEROFF: if (modem->config.configured && !modem->state.error) - osmo_fsm_inst_state_chg(fi, MODEM_ST_UNLOCK_PIN, 0, 0); + osmo_fsm_inst_state_chg(fi, MODEM_ST_WAIT_UIM, 0, 0); else /* stop timer and wait for the user to continue */ osmo_timer_del(&fi->timer); break; case MODEM_EV_REQ_CONFIGURED: /* when the modem reach this state, but isn't yet configured, wait for the config */ - osmo_fsm_inst_state_chg(fi, MODEM_ST_UNLOCK_PIN, 0, 0); + osmo_fsm_inst_state_chg(fi, MODEM_ST_WAIT_UIM, 0, 0); break; } } -static void modem_st_unlock_pin_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) +static void modem_st_wait_uim_onenter(struct osmo_fsm_inst *fi, uint32_t old_state) { - osmo_fsm_inst_state_chg(fi, MODEM_ST_CONFIGURE_MODEM, 0, 0); + struct modem *modem = fi->priv; + + if (modem->sim.fi->state == SIM_ST_READY) + osmo_fsm_inst_state_chg(fi, MODEM_ST_CONFIGURE_MODEM, 0, 0); } -static void modem_st_unlock_pin(struct osmo_fsm_inst *fi, uint32_t event, void *data) + +static void modem_st_wait_uim(struct osmo_fsm_inst *fi, uint32_t event, void *data) { + switch (event) { + case MODEM_EV_REQ_SIM_READY: + osmo_fsm_inst_state_chg(fi, MODEM_ST_CONFIGURE_MODEM, 0, 0); + break; + default: + break; + } } static void wds_modify_profile_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) @@ -739,7 +523,6 @@ static void modem_st_configure_modem(struct osmo_fsm_inst *fi, uint32_t event, v /* TODO: check if this is the default profile! */ } - static void wda_set_data_format_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) { struct modem *modem = req->cb_data; @@ -747,7 +530,8 @@ static void wda_set_data_format_cb(struct qmi_service *service, struct qmi_reque struct qmi_wda_set_data_format_response res = {}; if (req->ret) { - modem_log(modem, LOGL_ERROR, "Failed to set data format. Status %d/%s.", req->ret, qmi_get_error_str(req->ret)); + modem_log(modem, LOGL_ERROR, "Failed to set data format. Status %d/%s.", req->ret, + qmi_get_error_str(req->ret)); osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_FAILED, NULL); return; } @@ -772,7 +556,7 @@ static void wda_set_data_format_cb(struct qmi_service *service, struct qmi_reque return; } - osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_SUCCEED, (void *) (long) msg->svc.message); + osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_SUCCEED, (void *)(long)msg->svc.message); } /* Configure the kernel network interface */ @@ -1191,11 +975,6 @@ static int modem_fsm_timer_cb(struct osmo_fsm_inst *fi) uqmi_service_send_simple(service, qmi_set_nas_get_serving_system_request, get_serving_system_cb, modem); osmo_timer_schedule(&fi->timer, NAS_SERVICE_POLL_TIMEOUT_S, 0); break; - case MODEM_ST_GET_IMSI: - fi->N++; - tx_get_imsi_req(fi, MODEM_ST_GET_IMSI); - osmo_timer_schedule(&fi->timer, NAS_SERVICE_POLL_TIMEOUT_S, 0); - break; case MODEM_ST_START_IFACE: switch (fi->T) { case N_RESEND: @@ -1263,40 +1042,32 @@ static const struct osmo_fsm_state modem_states[] = { .onenter = modem_st_get_version_onenter, }, [MODEM_ST_GET_MODEL] = { - .in_event_mask = S(MODEM_EV_RX_MODEL) | S(MODEM_EV_RX_MANUFACTURER) | S(MODEM_EV_RX_REVISION), - .out_state_mask = S(MODEM_ST_GET_IMSI) | S(MODEM_ST_DESTROY), + .in_event_mask = S(MODEM_EV_RX_MODEL) | + S(MODEM_EV_RX_MANUFACTURER) | + S(MODEM_EV_RX_REVISION), + .out_state_mask = S(MODEM_ST_POWEROFF) | S(MODEM_ST_DESTROY), .name = "GET_MODEL", .action = modem_st_get_model, .onenter = modem_st_get_model_onenter, }, - [MODEM_ST_GET_IMSI] = { - .in_event_mask = S(MODEM_EV_RX_IMSI) - | S(MODEM_EV_RX_UIM_FAILED) - | S(MODEM_EV_RX_UIM_GET_SLOT_FAILED) - | S(MODEM_EV_RX_UIM_VALID_ICCID) - | S(MODEM_EV_RX_UIM_NO_UIM_FOUND) - | S(MODEM_EV_RX_IMSI_DMS_FAILED), - .out_state_mask = S(MODEM_ST_POWEROFF) | S(MODEM_ST_DESTROY), - .name = "GET_IMSI", - .action = modem_st_get_imsi, - .onenter = modem_st_get_imsi_onenter, - }, [MODEM_ST_POWEROFF] = { .in_event_mask = S(MODEM_EV_RX_POWEROFF) | S(MODEM_EV_RX_POWERON) | S(MODEM_EV_RX_POWERSET) | S(MODEM_EV_REQ_CONFIGURED), - .out_state_mask = S(MODEM_ST_UNLOCK_PIN) | S(MODEM_ST_DESTROY), + .out_state_mask = S(MODEM_ST_CONFIGURE_MODEM) + | S(MODEM_ST_DESTROY) + | S(MODEM_ST_WAIT_UIM), .name = "POWEROFF", .action = modem_st_poweroff, .onenter = modem_st_poweroff_onenter, }, - [MODEM_ST_UNLOCK_PIN] = { - .in_event_mask = S(MODEM_EV_RX_UNLOCKED_PIN), + [MODEM_ST_WAIT_UIM] = { + .in_event_mask = S(MODEM_EV_REQ_SIM_READY), .out_state_mask = S(MODEM_ST_CONFIGURE_MODEM) | S(MODEM_ST_DESTROY), - .name = "UNLOCK_PIN", - .action = modem_st_unlock_pin, - .onenter = modem_st_unlock_pin_onenter, + .name = "WAIT_UIM", + .action = modem_st_wait_uim, + .onenter = modem_st_wait_uim_onenter, }, [MODEM_ST_CONFIGURE_MODEM] = { .in_event_mask = S(MODEM_EV_RX_CONFIGURED) diff --git a/uqmid/modem_fsm.h b/uqmid/modem_fsm.h index 5eeb42a..50f45e8 100644 --- a/uqmid/modem_fsm.h +++ b/uqmid/modem_fsm.h @@ -6,9 +6,8 @@ enum modem_fsm_state { MODEM_ST_RESYNC, MODEM_ST_GET_VERSION, MODEM_ST_GET_MODEL, - MODEM_ST_GET_IMSI, MODEM_ST_POWEROFF, - MODEM_ST_UNLOCK_PIN, + MODEM_ST_WAIT_UIM, MODEM_ST_CONFIGURE_MODEM, MODEM_ST_CONFIGURE_KERNEL, MODEM_ST_POWERON, @@ -24,6 +23,10 @@ enum modem_fsm_event { MODEM_EV_REQ_START, MODEM_EV_REQ_CONFIGURED, + MODEM_EV_REQ_SIM_FAILURE, + MODEM_EV_REQ_SIM_READY, + MODEM_EV_REQ_SIM_TERM, + MODEM_EV_RX_SYNC, MODEM_EV_RX_VERSION, @@ -31,22 +34,10 @@ enum modem_fsm_event { MODEM_EV_RX_MANUFACTURER, MODEM_EV_RX_REVISION, - MODEM_EV_RX_IMSI, - MODEM_EV_RX_UIM_FAILED, - MODEM_EV_RX_UIM_GET_SLOT_FAILED, - MODEM_EV_RX_UIM_VALID_ICCID, - MODEM_EV_RX_UIM_NO_UIM_FOUND, - MODEM_EV_RX_IMSI_DMS_FAILED, - MODEM_EV_RX_POWEROFF, MODEM_EV_RX_POWERON, MODEM_EV_RX_POWERSET, - MODEM_EV_RX_UNLOCKED_PIN, - MODEM_EV_RX_UIM_PUK_REQUIRED, - MODEM_EV_RX_UIM_PIN_REQUIRED, - MODEM_EV_RX_UIM_READY, - MODEM_EV_RX_GET_PROFILE_LIST, MODEM_EV_RX_MODIFIED_PROFILE, MODEM_EV_RX_CONFIGURED, diff --git a/uqmid/modem_tx.c b/uqmid/modem_tx.c index 9698681..fb51698 100644 --- a/uqmid/modem_tx.c +++ b/uqmid/modem_tx.c @@ -212,3 +212,37 @@ int tx_wds_get_current_settings(struct modem *modem, struct qmi_service *wds, re req->cb_data = modem; return uqmi_service_send_msg(wds, req); } + +int tx_uim_read_transparent_file(struct modem *modem, struct qmi_service *uim, request_cb cb, uint16_t file_id, + uint8_t *filepath, unsigned int filepath_n) +{ + struct qmi_request *req = talloc_zero(uim, struct qmi_request); + struct qmi_msg *msg = talloc_zero_size(req, 1024); + + struct qmi_uim_read_transparent_request read_transparent_req = {}; + + read_transparent_req.set.file = true; + read_transparent_req.data.file.file_id = file_id; + read_transparent_req.data.file.file_path = filepath; + read_transparent_req.data.file.file_path_n = filepath_n; + + read_transparent_req.set.read_information = true; + read_transparent_req.data.read_information.length = 0; + read_transparent_req.data.read_information.offset = 0; + + read_transparent_req.set.session = 1; + read_transparent_req.data.session.session_type = 0; + read_transparent_req.data.session.application_identifier_n = 0; + read_transparent_req.data.session.application_identifier = NULL; + + int ret = qmi_set_uim_read_transparent_request(msg, &read_transparent_req); + if (ret) { + LOG_ERROR("Failed to encode read_transparent_file"); + return 1; + } + + req->msg = msg; + req->cb = cb; + req->cb_data = modem; + return uqmi_service_send_msg(uim, req); +} diff --git a/uqmid/modem_tx.h b/uqmid/modem_tx.h index 9302a65..5ef3615 100644 --- a/uqmid/modem_tx.h +++ b/uqmid/modem_tx.h @@ -10,6 +10,8 @@ struct qmi_service; int tx_dms_set_operating_mode(struct modem *modem, struct qmi_service *dms, uint8_t operating_mode, request_cb cb); int tx_nas_subscribe_nas_events(struct modem *modem, struct qmi_service *nas, bool action, request_cb cb); +int tx_uim_read_transparent_file(struct modem *modem, struct qmi_service *wds, request_cb cb, + uint16_t file_id, uint8_t *filepath, unsigned int filepath_n); int tx_wda_set_data_format(struct modem *modem, struct qmi_service *wda, request_cb cb); int tx_wds_get_profile_list(struct modem *modem, struct qmi_service *wds, request_cb cb); int tx_wds_modify_profile(struct modem *modem, struct qmi_service *wds, request_cb cb, uint8_t profile, const char *apn, diff --git a/uqmid/sim_fsm.c b/uqmid/sim_fsm.c new file mode 100644 index 0000000..6bad1ca --- /dev/null +++ b/uqmid/sim_fsm.c @@ -0,0 +1,551 @@ +/* + * uqmi -- tiny QMI support implementation + * + * Copyright (C) 2024 Alexander Couzens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + */ + +/* SIM/UIM card FSM + * + * tracks the state of the sim and notifies the parent on hard events. + * e.g. removal of the sim + */ + +#include "logging.h" +#include "modem.h" +#include "modem_tx.h" +#include "osmocom/fsm.h" +#include "osmocom/utils.h" +#include "sim_fsm.h" +#include "modem_fsm.h" +#include "qmi-enums.h" +#include "qmi-message.h" +#include "qmi-enums-uim.h" +#include "services.h" +#include "utils.h" + +#include + +#define S(x) (1 << (x)) + +#define SIM_DEFAULT_TIMEOUT_S 5 + +// move the sim configuration +// child fsm +// what happens when the sim goes down? + +#define MAX_PATH 3 +struct uim_file { + uint16_t file_id; + uint8_t path_n; /* count of elements in path */ + uint16_t path[MAX_PATH]; /* a path is a 16 bit integer */ +}; + +enum uim_file_names { + UIM_FILE_IMSI, + UIM_FILE_ICCID, +}; + +/* 3GPP TS 31.102 */ +static const struct uim_file uim_files[] = { + [UIM_FILE_IMSI] = { 0x6f07, 2, { 0x3f00, 0x7fff } }, + [UIM_FILE_ICCID] = { 0x2fe2, 1, { 0x3f00 } }, +}; + +static int _tx_uim_read_transparent_file(struct modem *modem, struct qmi_service *uim, request_cb cb, + enum uim_file_names file_name) +{ + uint8_t path[MAX_PATH * 2]; + const struct uim_file *file = &uim_files[file_name]; + + for (int i = 0, j = 0; i < file->path_n; i++, j += 2) { + path[j] = file->path[i] & 0xff; + path[j + 1] = file->path[i] >> 8; + } + + return tx_uim_read_transparent_file(modem, uim, cb, file->file_id, &path[0], file->path_n * 2); +} + +static void sim_st_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case SIM_EV_REQ_START: + osmo_fsm_inst_state_chg(fi, SIM_ST_GET_INFO, SIM_DEFAULT_TIMEOUT_S, 0); + break; + default: + break; + } +} + +static void sim_st_wait_uim_present(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void uim_get_slot_status_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +{ + struct modem *modem = req->cb_data; + int ret = 0, i = 0; + char *iccid_str; + + if (req->ret) { + modem_log(modem, LOGL_INFO, "Failed to get slot status. Returned %d/%s. Trying next card status", + req->ret, qmi_get_error_str(req->ret)); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_SLOT_FAILED, NULL); + return; + } + + struct qmi_uim_get_slot_status_response res = {}; + ret = qmi_parse_uim_get_slot_status_response(msg, &res); + if (ret) { + modem_log(modem, LOGL_INFO, "Failed to get slot status."); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_SLOT_FAILED, NULL); + return; + } + + for (i = 0; i < res.data.physical_slot_status_n; ++i) { + uint32_t physical_card_status = res.data.physical_slot_status[i].physical_card_status; + uint32_t physical_slot_status = res.data.physical_slot_status[i].physical_slot_status; + /* ignoring uint8_t logical_slot */ + unsigned int iccid_n = res.data.physical_slot_status[i].iccid_n; + uint8_t *iccid_bcd = res.data.physical_slot_status[i].iccid; + + if (physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT) + continue; + + if (physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE) + continue; + + if (iccid_n == 0) { + modem_log(modem, LOGL_INFO, "slot %d: Empty ICCID. UIM not yet ready? Or an EUICC?", i); + continue; + } + + /* FIXME: check for uneven number .. */ + int iccid_str_len = iccid_n * 2; + iccid_str = talloc_zero_size(modem, iccid_n * 2); + ret = osmo_bcd2str(iccid_str, iccid_str_len, iccid_bcd, 0, iccid_n, 1); + + if (ret) { + modem_log(modem, LOGL_INFO, "Couldn't decode ICCID of slot %d", i); + talloc_free(iccid_str); + continue; + } + + if (modem->iccid) { + modem_log(modem, LOGL_INFO, "Modem already contains an ICCID. Replacing old entry %s with %s", + modem->iccid, iccid_str); + talloc_free(modem->iccid); + } + modem->iccid = iccid_str; + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_VALID_ICCID, NULL); + } + + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_NO_UIM_FOUND, NULL); +} + +static void uim_get_card_status_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +{ + struct modem *modem = req->cb_data; + int ret = 0, i = 0, found = 0; + + if (req->ret) { + modem_log(modem, LOGL_INFO, "Failed to get card status %d/%s.", req->ret, qmi_get_error_str(req->ret)); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_FAILED, NULL); + return; + } + + struct qmi_uim_get_card_status_response res = {}; + ret = qmi_parse_uim_get_card_status_response(msg, &res); + if (ret) { + modem_log(modem, LOGL_INFO, "Failed to get card status. Decoder failed."); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_FAILED, NULL); + return; + } + + for (i = 0; i < res.data.card_status.cards_n; ++i) { + if (res.data.card_status.cards[i].card_state != QMI_UIM_CARD_STATE_PRESENT) + continue; + + for (int j = 0; j < res.data.card_status.cards[i].applications_n; ++j) { + /* TODO: We should prioritize the USIM application. If not found, fallback to SIM -> ISIM -> CSIM */ + if (res.data.card_status.cards[i].applications[j].type == QMI_UIM_CARD_APPLICATION_TYPE_UNKNOWN) + continue; + + modem_log(modem, LOGL_INFO, "Found valid card application type %x", + res.data.card_status.cards[i].applications[j].type); + modem->sim.use_upin = res.data.card_status.cards[i].applications[i].upin_replaces_pin1; + + /* check if pin1 or upin is the correct method */ + if (modem->sim.use_upin) { + modem->sim.state = uim_pin_to_uqmi_state(res.data.card_status.cards[i].upin_state); + modem->sim.pin_retries = res.data.card_status.cards[i].upin_retries; + modem->sim.puk_retries = res.data.card_status.cards[i].upuk_retries; + } else { + modem->sim.state = + uim_pin_to_uqmi_state(res.data.card_status.cards[i].applications[i].pin1_state); + modem->sim.pin_retries = res.data.card_status.cards[i].applications[i].pin1_retries; + modem->sim.puk_retries = res.data.card_status.cards[i].applications[i].puk1_retries; + } + + found = 1; + break; /* handle first application only for now */ + } + + if (found) { + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_CARD_STATUS_VALID, NULL); + return; + } + } + + modem_log(modem, LOGL_INFO, "Failed to find a valid UIM in get_status. Failing UIM."); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_FAILED, NULL); +} + +static void uim_read_imsi_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +{ + struct modem *modem = req->cb_data; + int ret = 0; + + if (req->ret) { + modem_log(modem, LOGL_INFO, "Failed to read imsi. Qmi Ret %d/%s.", req->ret, + qmi_get_error_str(req->ret)); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_IMSI_FAILED, NULL); + return; + } + + struct qmi_uim_read_record_response res = {}; + ret = qmi_parse_uim_read_record_response(msg, &res); + if (ret) { + modem_log(modem, LOGL_INFO, "Failed to read imsi. Decoder failed. %d", ret); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_IMSI_FAILED, NULL); + return; + } + + /* TODO: card result on 5g without imsi? */ + // if (res.set.card_result) { + // modem_log(modem, LOGL_INFO, "Failed to read imsi. Card Result."); + // return; + // } + + if (!res.data.read_result_n) { + modem_log(modem, LOGL_INFO, "Failed to read imsi. No Read Data"); + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_FAILED, NULL); + return; + } + + // where to get a BCD decoder? + ret = uqmi_sim_decode_imsi(res.data.read_result, res.data.read_result_n, modem->imsi, sizeof(modem->imsi)); + if (ret < 0) + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_IMSI_FAILED, NULL); + + osmo_fsm_inst_dispatch(modem->sim.fi, SIM_EV_RX_UIM_GET_IMSI_SUCCESS, NULL); +} + +static void sim_st_get_info_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + struct modem *modem = fi->priv; + + struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); + // struct qmi_service *dms = uqmi_service_find(modem->qmi, QMI_SERVICE_DMS); + + // should we use uim and have it available? + if (uim && modem->sim.use_uim) { + modem_log(modem, LOGL_INFO, "Trying to query UIM for slot status."); + uqmi_service_send_simple(uim, qmi_set_uim_get_slot_status_request, uim_get_slot_status_cb, modem); + return; + } else { + // dms + } +} + +static void sim_st_get_info(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct modem *modem = fi->priv; + + struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); + + switch (event) { + case SIM_EV_RX_UIM_GET_SLOT_FAILED: + case SIM_EV_RX_UIM_VALID_ICCID: + case SIM_EV_RX_UIM_NO_UIM_FOUND: + /* Get Slot Status succeeded? */ + if (uim && modem->sim.use_uim) + uqmi_service_send_simple(uim, qmi_set_uim_get_card_status_request, uim_get_card_status_cb, + modem); + break; + case SIM_EV_RX_UIM_CARD_STATUS_VALID: + if (uim && modem->sim.use_uim) { + _tx_uim_read_transparent_file(modem, uim, uim_read_imsi_cb, UIM_FILE_IMSI); + } + break; + case SIM_EV_RX_UIM_GET_IMSI_FAILED: + case SIM_EV_RX_UIM_GET_IMSI_SUCCESS: + switch (modem->sim.state) { + case UQMI_SIM_PIN_REQUIRED: + osmo_fsm_inst_state_chg(fi, SIM_ST_CHV_PIN, SIM_DEFAULT_TIMEOUT_S, 0); + break; + case UQMI_SIM_PUK_REQUIRED: + osmo_fsm_inst_state_chg(fi, SIM_ST_CHV_PUK, SIM_DEFAULT_TIMEOUT_S, 0); + break; + case UQMI_SIM_READY: + osmo_fsm_inst_state_chg(fi, SIM_ST_READY, 0, 0); + break; + case UQMI_SIM_UNKNOWN: + /* FIXME: what to do when unknown? */ + break; + case UQMI_SIM_BLOCKED: + osmo_fsm_inst_state_chg(fi, SIM_ST_FAILED, SIM_DEFAULT_TIMEOUT_S, 0); + break; + default: + /* FIXME: default case */ + break; + } + break; + } +} + +static void sim_st_chv_pin_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + // FIXME: try to unlock if enough tries are there +} + +static void sim_st_chv_pin(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_chv_puk_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + // FIXME: try to unlock if enough tries are there +} + +static void sim_st_chv_puk(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_ready_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + struct modem *modem = fi->priv; + + osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_REQ_SIM_READY, NULL); +} + +static void sim_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_failed(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ +} + +static void sim_st_destroy_on_enter(struct osmo_fsm_inst *fi, uint32_t old_state) +{ + // TODO: deregister indications?, wait x seconds, then terminate + // in theory we don't have to because the modem should close the registration when closing + // the uim service +} + +static void sim_st_destroy(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case SIM_EV_RX_FAILED: + break; + case SIM_EV_RX_SUCCEED: + break; + } + // terminate here when the derg +} + +static int sim_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + // struct modem *modem = fi->priv; + // struct qmi_service *service; + + switch (fi->state) { + default: + switch (fi->T) { + default: + break; + } + break; + } + + return 0; +} + +static void sim_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case SIM_EV_REQ_DESTROY: + osmo_fsm_inst_state_chg(fi, SIM_ST_DESTROY, 0, 0); + break; + } +} + +static const struct value_string sim_event_names[] = { + { SIM_EV_REQ_START, "REQ_START" }, + { SIM_EV_REQ_CONFIGURED, "REQ_CONFIGURED" }, + { SIM_EV_REQ_DESTROY, "REQ_DESTROY" }, + { SIM_EV_RX_UIM_FAILED, "RX_UIM_FAILED" }, + { SIM_EV_RX_UIM_GET_SLOT_FAILED, "RX_UIM_GET_SLOT_FAILED" }, + { SIM_EV_RX_UIM_NO_UIM_FOUND, "RX_UIM_NO_UIM_FOUND" }, + { SIM_EV_RX_UIM_VALID_ICCID, "RX_UIM_VALID_ICCID" }, + { SIM_EV_RX_UIM_CARD_STATUS_VALID, "RX_UIM_CARD_STATUS_VALID" }, + { SIM_EV_RX_UIM_GET_IMSI_SUCCESS, "RX_UIM_GET_IMSI_SUCCESS" }, + { SIM_EV_RX_UIM_GET_IMSI_FAILED, "RX_UIM_GET_IMSI_FAILED" }, + { SIM_EV_RX_UIM_PIN_REQUIRED, "RX_UIM_PIN_REQUIRED" }, + { SIM_EV_RX_UIM_PUK_REQUIRED, "RX_UIM_PUK_REQUIRED" }, + { SIM_EV_RX_UIM_READY, "RX_UIM_READY" }, + { SIM_EV_RX_FAILED, "RX_FAILED" }, + { SIM_EV_RX_SUCCEED, "RX_SUCCEED" }, + { 0, NULL }, +}; + +static const struct osmo_fsm_state sim_states[] = { + [SIM_ST_IDLE] = { + .in_event_mask = S(SIM_EV_REQ_START), + .out_state_mask = S(SIM_ST_GET_INFO) | S(SIM_ST_DESTROY), + .name = "IDLE", + .action = sim_st_idle, + }, + [SIM_ST_WAIT_UIM_PRESENT] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "WAIT UIM PRESENT", + .action = sim_st_wait_uim_present, + }, + [SIM_ST_GET_INFO] = { + .in_event_mask = S(SIM_EV_RX_UIM_GET_SLOT_FAILED) | + S(SIM_EV_RX_UIM_VALID_ICCID) | + S(SIM_EV_RX_UIM_NO_UIM_FOUND) | + S(SIM_EV_RX_UIM_CARD_STATUS_VALID) | + S(SIM_EV_RX_UIM_GET_IMSI_SUCCESS) | + S(SIM_EV_RX_UIM_GET_IMSI_FAILED), + .out_state_mask = S(SIM_ST_READY) | + S(SIM_ST_CHV_PIN) | + S(SIM_ST_CHV_PUK) | + S(SIM_ST_DESTROY), + .name = "GET_INFO", + .onenter = sim_st_get_info_on_enter, + .action = sim_st_get_info, + }, + [SIM_ST_READY] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "READY", + .onenter = sim_st_ready_on_enter, + .action = sim_st_ready, + }, + [SIM_ST_CHV_PIN] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_CHV_PUK) | S(SIM_ST_GET_INFO), + .name = "CHV_PIN", + .onenter = sim_st_chv_pin_on_enter, + .action = sim_st_chv_pin, + }, + [SIM_ST_CHV_PUK] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_CHV_PIN) | S(SIM_ST_GET_INFO), + .name = "CHV_PIN", + .onenter = sim_st_chv_puk_on_enter, + .action = sim_st_chv_puk, + }, + [SIM_ST_FAILED] = { + .in_event_mask = 0, + .out_state_mask = S(SIM_ST_DESTROY) | S(SIM_ST_GET_INFO), + .name = "FAILED", + .action = sim_st_failed, + }, + [SIM_ST_DESTROY] = { + .in_event_mask = 0, + .out_state_mask = 0, + .name = "DESTROY", + .action = sim_st_destroy, + .onenter = sim_st_destroy_on_enter, + }, +}; + +static struct osmo_fsm sim_fsm = { + .name = "SIM", + .states = sim_states, + .num_states = ARRAY_SIZE(sim_states), + .allstate_event_mask = S(SIM_EV_REQ_DESTROY), + .allstate_action = sim_fsm_allstate_action, + .timer_cb = sim_fsm_timer_cb, + .event_names = sim_event_names, + .pre_term = NULL, +}; + +struct osmo_fsm_inst *sim_fsm_alloc(struct modem *modem) +{ + struct osmo_fsm_inst *fi = osmo_fsm_inst_alloc_child(&sim_fsm, modem->fi, MODEM_EV_REQ_SIM_TERM); + if (fi) + fi->priv = modem; + + return fi; +} + +static __attribute__((constructor)) void on_dso_load_ctx(void) +{ + OSMO_ASSERT(osmo_fsm_register(&sim_fsm) == 0); +} + +// just to be removed later + +// static void +// dms_get_imsi_cb(struct qmi_service *service, struct qmi_request *req, struct qmi_msg *msg) +// { +// struct modem *modem = req->cb_data; +// int ret = 0; + +// struct qmi_dms_uim_get_imsi_response res = {}; +// ret = qmi_parse_dms_uim_get_imsi_response(msg, &res); + +// if (ret) { +// modem_log(modem, LOGL_INFO, "Failed to get imsi via dms. Failed to parse message"); +// osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI_DMS_FAILED, NULL); +// return; +// } + +// if (!res.data.imsi || strlen(res.data.imsi)) { +// modem_log(modem, LOGL_INFO, "Received empty IMSI"); +// osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI_DMS_FAILED, (void *) 1); +// return; +// } + +// strncpy(modem->imsi, res.data.imsi, 15); +// osmo_fsm_inst_dispatch(modem->fi, MODEM_EV_RX_IMSI, NULL); +// } + +// static void tx_get_imsi_req(struct osmo_fsm_inst *fi, uint32_t old_state) +// { +// struct modem *modem = fi->priv; +// struct qmi_service *uim = uqmi_service_find(modem->qmi, QMI_SERVICE_UIM); + +// /* FIXME: register for indication of removed uims +// * - register_physical_slot_status_events +// * - Physical Slot Status? +// */ +// if (uim) { +// modem_log(modem, LOGL_INFO, "Trying to query UIM for slot status."); +// uqmi_service_send_simple(uim, qmi_set_uim_get_slot_status_request, uim_get_slot_status_cb, modem); +// return; +// } + +// /* Retrieve it via DMS */ +// struct qmi_service *dms = uqmi_service_find(modem->qmi, QMI_SERVICE_DMS); +// if (dms) { +// modem_log(modem, LOGL_INFO, "Trying to query UIM for IMSI."); +// uqmi_service_send_simple(dms, qmi_set_dms_uim_get_imsi_request, dms_get_imsi_cb, modem); +// return; +// } +// } diff --git a/uqmid/sim_fsm.h b/uqmid/sim_fsm.h new file mode 100644 index 0000000..4577cab --- /dev/null +++ b/uqmid/sim_fsm.h @@ -0,0 +1,54 @@ +#ifndef __UQMID_SIM_FSM_H +#define __UQMID_SIM_FSM_H + +#include +enum sim_fsm_state { + SIM_ST_IDLE, + SIM_ST_WAIT_UIM_PRESENT, + SIM_ST_GET_INFO, + SIM_ST_CHV_PIN, + SIM_ST_CHV_PUK, + SIM_ST_READY, + SIM_ST_FAILED, + SIM_ST_REMOVED, + SIM_ST_DESTROY, +}; + +// check uim available (or not configured to be used) +// uim get slot status, uim get card status +// uim check if pin is required, check if puk is required +// uim get IMSI, ICCID, MSISDN? +// setup indication to be notified if sim got removed + +// sending information towards modem_fsm-> (removed, ready) + +enum sim_fsm_event { + SIM_EV_REQ_START, + SIM_EV_REQ_CONFIGURED, + SIM_EV_REQ_DESTROY, + + SIM_EV_RX_UIM_FAILED, /* UIM failed, need to switch to DMS */ + + SIM_EV_RX_UIM_GET_SLOT_FAILED, /* some modem doesn't support GET SLOT */ + SIM_EV_RX_UIM_NO_UIM_FOUND, /* we couldn't find a UIM */ + SIM_EV_RX_UIM_VALID_ICCID, /* when Get Slot Status found a valid ICCID */ + SIM_EV_RX_UIM_CARD_STATUS_VALID, /* Got a valid Get Card Status */ + + SIM_EV_RX_UIM_GET_IMSI_SUCCESS, + SIM_EV_RX_UIM_GET_IMSI_FAILED, + + SIM_EV_RX_UIM_PIN_REQUIRED, + SIM_EV_RX_UIM_PUK_REQUIRED, + SIM_EV_RX_UIM_READY, + + SIM_EV_RX_FAILED, + SIM_EV_RX_SUCCEED, /* a generic callback succeeded */ +}; + +struct modem; +struct osmo_fsm_inst; + +void sim_fsm_start(struct modem *modem); +struct osmo_fsm_inst *sim_fsm_alloc(struct modem *modem); + +#endif /* __UQMID_SIM_FSM_H */ diff --git a/uqmid/ubus.c b/uqmid/ubus.c index 546b177..6e3c512 100644 --- a/uqmid/ubus.c +++ b/uqmid/ubus.c @@ -414,6 +414,7 @@ static int modem_dump_state(struct ubus_context *ctx, struct ubus_object *obj, s blobmsg_add_string(&b, "name", modem->name); blobmsg_add_string(&b, "device", modem->device); blobmsg_add_u32(&b, "state", modem->fi->state); + blobmsg_add_string(&b, "state_name", osmo_fsm_inst_state_name(modem->fi)); BLOBMSG_ADD_STR_CHECK(&b, "manufactor", modem->manuf); BLOBMSG_ADD_STR_CHECK(&b, "model", modem->model); BLOBMSG_ADD_STR_CHECK(&b, "rev", modem->rev); @@ -431,6 +432,7 @@ static int modem_dump_state(struct ubus_context *ctx, struct ubus_object *obj, s /* sim */ /* TODO: add human readable enum values */ blobmsg_add_u16(&b, "sim_state", modem->sim.state); + blobmsg_add_string(&b, "sim_state_name", osmo_fsm_inst_state_name(modem->sim.fi)); blobmsg_add_u8(&b, "sim_use_upin", modem->sim.use_upin); blobmsg_add_u16(&b, "sim_pin_retries", modem->sim.pin_retries & 0xff); blobmsg_add_u16(&b, "sim_puk_retries", modem->sim.puk_retries & 0xff); -- 2.30.2