uqmid: move usim related function into own fsm
authorAlexander Couzens <[email protected]>
Sun, 8 Sep 2024 15:50:06 +0000 (17:50 +0200)
committerDavid Bauer <[email protected]>
Sat, 31 May 2025 20:41:00 +0000 (22:41 +0200)
Signed-off-by: Alexander Couzens <[email protected]>
uqmid/CMakeLists.txt
uqmid/modem.c
uqmid/modem.h
uqmid/modem_fsm.c
uqmid/modem_fsm.h
uqmid/modem_tx.c
uqmid/modem_tx.h
uqmid/sim_fsm.c [new file with mode: 0644]
uqmid/sim_fsm.h [new file with mode: 0644]
uqmid/ubus.c

index b2e672e71fee3e1226eb68f4c62dedb69b056113..23e8d0666349482a0ee71d805c4a578f3e30cded 100644 (file)
@@ -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})
index 71927b825096c4cff4068b2d3b91353b3ec9f744..d9357235fd0ef75e675171f84fc37b9beb279f14 100644 (file)
@@ -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);
 
index 144f9614f2604bbf4c290754a1712249ac5c50fe..78cc7fe20f45ae9bc3c5538374224d76d9a0d5da 100644 (file)
@@ -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;
 };
 
index b85f53820a7e02eab8549d62a81a044a2e9ab5a1..045f82c5a98d2c68a0ea5e19d7ad3d4ec3083db1 100644 (file)
@@ -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)
index 5eeb42a9b168393c0a0c937b62c968902edbbb46..50f45e87d140fdd8be8fc9756f05b8924fec2cda 100644 (file)
@@ -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,
index 96986814839ab01a9e6155c77ca7668dda563acd..fb51698c8d14ae7ecfb22a3a465bf91386c5db8d 100644 (file)
@@ -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);
+}
index 9302a65eb32f4a4b944a7864f523b508add9f9bc..5ef361506d5eb8114d444ff33a6396c65a90661a 100644 (file)
@@ -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 (file)
index 0000000..6bad1ca
--- /dev/null
@@ -0,0 +1,551 @@
+/*
+ * uqmi -- tiny QMI support implementation
+ *
+ * Copyright (C) 2024 Alexander Couzens <[email protected]>
+ *
+ * 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 <talloc.h>
+
+#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 (file)
index 0000000..4577cab
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef __UQMID_SIM_FSM_H
+#define __UQMID_SIM_FSM_H
+
+#include <stdbool.h>
+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 */
index 546b177dbf9023f67d6951affb9a29d00bfc463d..6e3c5123b809771053b44c4e3eb6bb59b26dc6e9 100644 (file)
@@ -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);