Re: [PATCH v8 7/8] mbim: Add support for SIM I/O operations

Andres Salomon <[email protected]> Sat, 13 Dec 2025 05:06:03 -0500
Newsgroups dev.linux.lists.ofono
Message-ID <[email protected]>
On 12/12/25 16:02, Muhammad Asif wrote:
> Adds support for complete SIM I/O operations, with complete path
> resolution using sim_ef_db.
[...]
> --- a/drivers/mbimmodem/sim.c
> +++ b/drivers/mbimmodem/sim.c
> @@ -22,14 +22,39 @@
>   #include "drivers/mbimmodem/mbim-message.h"
>   #include "drivers/mbimmodem/mbimmodem.h"
>   
> +struct sim_app {
> +	enum mbim_app_type app_type;
> +	uint32_t aid_len;
> +	uint8_t *aid;
> +	char *label;
> +	uint32_t pin_key_references;
> +};
> +
>   struct sim_data {
>   	struct mbim_device *device;
> +	uint32_t app_count;
> +	uint32_t active_app;
> +	struct sim_app *apps;
>   	char *iccid;
>   	char *imsi;
>   	uint32_t last_pin_type;
>   	bool present : 1;
>   };
>   
> +static uint32_t mbim_file_structure_to_ofono(uint32_t file_structure)
> +{
> +	switch (file_structure) {
> +	case 1:
> +		return OFONO_SIM_FILE_STRUCTURE_TRANSPARENT;
> +	case 2:
> +		return OFONO_SIM_FILE_STRUCTURE_CYCLIC;
> +	case 3:
> +		return OFONO_SIM_FILE_STRUCTURE_FIXED;
> +	default:
> +		return OFONO_SIM_FILE_STRUCTURE_TRANSPARENT;
> +	}
> +}
> +
>   static void mbim_sim_state_changed(struct ofono_sim *sim, uint32_t ready_state)
>   {
>   	struct sim_data *sd = ofono_sim_get_data(sim);
> @@ -76,6 +101,136 @@ static void mbim_read_imsi(struct ofono_sim *sim,
>   	CALLBACK_WITH_SUCCESS(cb, sd->imsi, user_data);
>   }
>   
> +static void read_file_info_cb(struct mbim_message *message, void *user_data)
> +{
> +	struct cb_data *cbd = user_data;
> +	ofono_sim_file_info_cb_t cb = cbd->cb;
> +	unsigned char access[3] = {0x0f, 0xff, 0xff};
> +	uint32_t version, status_word1, status_word2, file_accessibility;
> +	uint32_t file_type, file_structure, file_item_count, file_item_size;
> +
> +	if (!mbim_message_get_error(message)) {
> +		mbim_message_get_arguments(message, "uuuuuuuu", &version, &status_word1,

This can fail, and the return value should be checked.


> +					&status_word2, &file_accessibility,
> +					&file_type, &file_structure,
> +					&file_item_count, &file_item_size);
> +
> +		CALLBACK_WITH_SUCCESS(cb, file_item_size,
> +					mbim_file_structure_to_ofono(file_structure),
> +					file_item_size * file_item_count,
> +					access, file_accessibility, cbd->data);
> +	} else {
> +		CALLBACK_WITH_FAILURE(cb, -1, -1, -1, NULL, -1, cbd->data);
> +	}
> +}
> +
> +static void mbim_read_file_info(struct ofono_sim *sim,
> +				int fileid, const unsigned char *path,
> +				unsigned int path_len,
> +				ofono_sim_file_info_cb_t cb, void *user_data)
> +{
> +	struct sim_data *data = ofono_sim_get_data(sim);
> +	struct cb_data *cbd = cb_data_new(cb, user_data);
> +	struct mbim_message *message;
> +	uint8_t *file_id_buf;
> +	int file_id_len;
> +
> +	message = mbim_message_new(mbim_ms_uicc_low_level_access,
> +					MBIM_CID_MS_UICC_LOW_LEVEL_ACCESS_FILE_STATUS,
> +					MBIM_COMMAND_TYPE_QUERY);
> +
> +	file_id_buf = mbim_get_fileid(data->apps[data->active_app].app_type, fileid, &file_id_len);
> +	mbim_message_set_arguments(message, "uayay", 1,
> +					data->apps[data->active_app].aid_len,
> +					data->apps[data->active_app].aid,
> +					file_id_len, file_id_buf);
> +	l_free(file_id_buf);
> +
> +	mbim_device_send(data->device, SIM_GROUP, message, read_file_info_cb, cbd, l_free);
> +}
> +
> +static void read_file_cb(struct mbim_message *message, void *user_data)
> +{
> +	struct cb_data *cbd = user_data;
> +	ofono_sim_read_cb_t cb = cbd->cb;
> +	struct mbim_message_iter iter;
> +	uint32_t version, status_word1, status_word2, data_size;
> +	uint8_t *data = NULL;
> +	int i = 0;
> +
> +	if (!mbim_message_get_error(message)) {
> +		mbim_message_get_arguments(message, "uuuAy", &version,

This can fail, and the return value should be checked.

> +						&status_word1, &status_word2, &iter);
> +
> +		data_size = iter.n_elem;
> +		data = l_malloc(data_size);
> +		while (mbim_message_iter_next_entry(&iter, data + i))
> +			i++;
> +
> +		CALLBACK_WITH_SUCCESS(cb, data, data_size, cbd->data);
> +	} else {
> +		CALLBACK_WITH_FAILURE(cb, NULL, 0, cbd->data);
> +	}
> +
> +	/* The data gets copied by ofono's sim driver, so we can free it */
> +	if (data)
> +		l_free(data);
> +}
> +
> +static void mbim_read_file_transparent(struct ofono_sim *sim,
> +					int fileid, int start, int length,
> +					const unsigned char *path, unsigned int path_len,
> +					ofono_sim_read_cb_t cb, void *user_data)
> +{
> +	struct sim_data *sd = ofono_sim_get_data(sim);
> +	struct cb_data *cbd = cb_data_new(cb, user_data);
> +	struct mbim_message *message;
> +	uint8_t *file_id_buf;
> +	int file_id_len;
> +
> +	message = mbim_message_new(mbim_ms_uicc_low_level_access,
> +					MBIM_CID_MS_UICC_LOW_LEVEL_ACCESS_READ_BINARY,
> +					MBIM_COMMAND_TYPE_QUERY);
> +
> +	file_id_buf = mbim_get_fileid(sd->apps[sd->active_app].app_type, fileid, &file_id_len);
> +	mbim_message_set_arguments(message, "uayayuusay", 1,
> +					sd->apps[sd->active_app].aid_len,
> +					sd->apps[sd->active_app].aid,
> +					file_id_len, file_id_buf, start,
> +					length, "", 0, NULL);
> +	l_free(file_id_buf);
> +
> +	mbim_device_send(sd->device, SIM_GROUP, message,
> +				read_file_cb, cbd, l_free);
> +}
> +
> +static void mbim_read_file_fixed_cyclic(struct ofono_sim *sim,
> +					int fileid, int record, int length,
> +					const unsigned char *path, unsigned int path_len,
> +					ofono_sim_read_cb_t cb, void *data)
> +{
> +	struct sim_data *sd = ofono_sim_get_data(sim);
> +	struct cb_data *cbd = cb_data_new(cb, data);
> +	struct mbim_message *message;
> +	uint8_t *file_id_buf;
> +	int file_id_len;
> +
> +	message = mbim_message_new(mbim_ms_uicc_low_level_access,
> +					MBIM_CID_MS_UICC_LOW_LEVEL_ACCESS_READ_RECORD,
> +					MBIM_COMMAND_TYPE_QUERY);
> +
> +	file_id_buf = mbim_get_fileid(sd->apps[sd->active_app].app_type, fileid, &file_id_len);
> +	mbim_message_set_arguments(message, "uayayusay", 1,
> +					sd->apps[sd->active_app].aid_len,
> +					sd->apps[sd->active_app].aid,
> +					file_id_len, file_id_buf, record,
> +					"", 0, NULL);
> +	l_free(file_id_buf);
> +
> +	mbim_device_send(sd->device, SIM_GROUP, message,
> +				read_file_cb, cbd, l_free);
> +}
> +
>   static enum ofono_sim_password_type mbim_pin_type_to_sim_password(
>   							uint32_t pin_type)
>   {
> @@ -473,6 +628,47 @@ error:
>   	ofono_sim_remove(sim);
>   }
>   
> +static void mbim_sim_list_apps_cb(struct mbim_message *message,
> +					void *user)
> +{
> +	struct sim_data *data = ofono_sim_get_data(user);
> +	uint32_t version, count, active_app;
> +	uint32_t application_id, size, pin_key_references;
> +	char *aid_label;
> +	struct mbim_message_iter iter, aid_iter, pin_iter;
> +	int i = 0;
> +

For correctness, set data->app_count = 0 and data->apps = NULL; here 
instead of below in the mbim_message_get_arguments() error path.

Then you can just return in the case of errors in the next two function 
calls.


> +	if (mbim_message_get_error(message))
> +		return;
> +
> +	if (!mbim_message_get_arguments(message, "uuuua(uAysuAy)", &version,
> +					&count, &active_app, &size, &iter)) {
> +		data->apps = NULL;
> +		data->app_count = 0;
> +		return;
> +	}

Oh, and one more thing; you might want to verify that count is a 
reasonable value (and return if > $bignumber. I don't know how often you 
might get nonsense from the sim, but.. Likewise, sanity checking for 
active_app (active_app < app_acount).


> +
> +	data->apps = l_new(struct sim_app, count);
> +	data->active_app = active_app;
> +	data->app_count = count;
> +

Personally I'd use a for loop here for clarity, but that's really up to 
you. But if you stick with the while() loop, I would set i=0 here to 
make it clearer what's going on.

> +	while (mbim_message_iter_next_entry(&iter, &application_id, &aid_iter,
> +					&aid_label, &pin_key_references, &pin_iter)) {
 > +		int j = 0;> +
> +		data->apps[i].app_type = application_id;
> +		data->apps[i].aid_len = aid_iter.n_elem;
> +		data->apps[i].aid = l_malloc(aid_iter.n_elem);


In the mbim_sim_remove function below, you free apps[i].label. But here 
you're getting label from the message_iter, and also allocating 
apps[i].aid but never free it. That's a bit confusing.. Did you mean to 
g_strdup(aid_label), and also l_free(sd->apps[i].aid) in the remove 
function below?


> +
> +		while (mbim_message_iter_next_entry(&aid_iter,
> +							data->apps[i].aid + j))
> +			j++;
> +
> +		data->apps[i].label = aid_label;
> +		i++;
> +	}
> +}
> +
>   static int mbim_sim_probe(struct ofono_sim *sim, unsigned int vendor,
>   				void *data)
>   {
> @@ -480,6 +676,14 @@ static int mbim_sim_probe(struct ofono_sim *sim, unsigned int vendor,
>   	struct mbim_message *message;
>   	struct sim_data *sd;
>   
> +	message = mbim_message_new(mbim_ms_uicc_low_level_access,
> +					MBIM_CID_MS_UICC_LOW_LEVEL_ACCESS_APPLICATION_LIST,
> +					MBIM_COMMAND_TYPE_QUERY);

For consistency, I would recommend verifying that message != NULL here 
as is done later in the function. It's a probe function, I'm guessing 
someone decided that it's a really good time to bail out if memory is tight.



> +	mbim_message_set_arguments(message, "");
> +
> +	mbim_device_send(device, SIM_GROUP, message,
> +			mbim_sim_list_apps_cb, sim, NULL);
> +
>   	message = mbim_message_new(mbim_uuid_basic_connect,
>   					MBIM_CID_SUBSCRIBER_READY_STATUS,
>   					MBIM_COMMAND_TYPE_QUERY);
> @@ -514,12 +718,22 @@ static void mbim_sim_remove(struct ofono_sim *sim)
>   
>   	l_free(sd->iccid);
>   	l_free(sd->imsi);
> +
> +	for (int i = 0; i < sd->app_count; i++)
> +		l_free(sd->apps[i].label);
> +
> +	l_free(sd->apps);
> +
>   	l_free(sd);
>   }
>   
>   static const struct ofono_sim_driver driver = {
>   	.probe			= mbim_sim_probe,
>   	.remove			= mbim_sim_remove,
> +	.read_file_info		= mbim_read_file_info,
> +	.read_file_transparent	= mbim_read_file_transparent,
> +	.read_file_cyclic	= mbim_read_file_fixed_cyclic,
> +	.read_file_linear	= mbim_read_file_fixed_cyclic,
>   	.read_imsi		= mbim_read_imsi,
>   	.query_passwd_state	= mbim_pin_query,
>   	.query_pin_retries	= mbim_pin_retries_query,
> diff --git a/drivers/mbimmodem/util.c b/drivers/mbimmodem/util.c
> index 4a3d9627..3280c863 100644
> --- a/drivers/mbimmodem/util.c
> +++ b/drivers/mbimmodem/util.c
> @@ -9,6 +9,7 @@
>   #include <stdbool.h>
>   
>   #include "src/common.h"
> +#include "simutil.h"
>   #include "mbim.h"
>   #include "util.h"
>   
> @@ -37,3 +38,33 @@ int mbim_data_class_to_tech(uint32_t n)
>   
>   	return -1;
>   }
> +
> +uint8_t *mbim_get_fileid(enum mbim_app_type app_type, uint32_t fileid, int *file_id_len)

Maybe mbim_get_fileid_path(), similar to isimodem's naming? Or 
mbim_get_fileid_path_new(), to make it clear that the returned memory 
should be freed by the caller?


> +{
> +	uint8_t parent_path[6] = {0};
> +	int fileid_len = 0;
> +	uint8_t *full_path;
> +
> +	if (app_type == MBIM_APP_USIM || app_type == MBIM_APP_ISIM)
> +		sim_ef_db_get_path_3g(fileid, parent_path);
> +	else
> +		sim_ef_db_get_path_2g(fileid, parent_path);


We should probably make sure sim_ef_db_get_path*() calls don't fail; if 
the result is < 2, I'm not sure what you should do.

> +
> +	/* Copy the parent_path up until the first pair of 0's */
> +	for (int i = 0; i < 6; i += 2) {
> +		if (i != 0 && parent_path[i] == 0x00

So you're always skipping over the first iteration of this loop? In that 
case, maybe start at 2 for clarity?  Eg,
		len = sim_ef_db_get_path_2g(...);

	if (len < 2 || len > 6) {
		/* set *file_id_len = -1 and return NULL?  set *file_id_len = 2, 
allocate an empty 2 byte full_path, fill it with fileid, and return it? */
	}

	for (i = 2; i < len; i += 2) {
		if (parent_path[i] == 0x0 && parent_path[i+1] == 0x0) {
			fileid_len = i;
			break;
		}
	}

> +			&& parent_path[i + 1] == 0x00) {
> +			fileid_len = i;
> +			break;
> +		}
> +	}
> +
> +	full_path = l_malloc(fileid_len + 2);
> +	memcpy(full_path, parent_path, fileid_len);
> +
> +	full_path[fileid_len] = (fileid >> 8) & 0xFF;
> +	full_path[fileid_len + 1] = fileid & 0xFF;
> +	*file_id_len = fileid_len + 2;
> +
> +	return full_path;
> +}
> diff --git a/drivers/mbimmodem/util.h b/drivers/mbimmodem/util.h
> index 2172cd9f..55669450 100644
> --- a/drivers/mbimmodem/util.h
> +++ b/drivers/mbimmodem/util.h
> @@ -43,3 +43,4 @@ static inline struct cb_data *cb_data_new(void *cb, void *data)
>   	} while (0)
>   
>   int mbim_data_class_to_tech(uint32_t n);
> +uint8_t *mbim_get_fileid(enum mbim_app_type app_type, uint32_t fileid, int *file_id_len);