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})
#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"
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);
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 {
} 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;
struct list_head list;
struct ubus_object ubus;
+ /*! modem_fsm instance */
struct osmo_fsm_inst *fi;
};
#include "qmi-enums-dms.h"
#include "qmi-errors.h"
+#include "sim_fsm.h"
#include "talloc.h"
#include "qmi-struct.h"
};
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 }
};
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;
}
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;
}
}
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)
/* 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;
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;
}
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 */
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:
.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)
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,
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,
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,
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);
+}
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,
--- /dev/null
+/*
+ * uqmi -- tiny QMI support implementation
+ *
+ *
+ * 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;
+// }
+// }
--- /dev/null
+#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 */
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);
/* 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);