Re: [PATCH] plugins: droid: migrate to QMI from AT

Denis Kenzior <[email protected]> Thu, 24 Jul 2025 17:25:51 -0500
Newsgroups dev.linux.lists.ofono
Message-ID <[email protected]>
Hi Ivaylo,

On 7/24/25 12:32 AM, Ivaylo Dimitrov wrote:
> Drop GAtChat usage and move to qmimodem
> ---
>   plugins/droid.c  | 487 +++++++++++++++++++++++++++++++++++++++++------
>   plugins/udevng.c |  30 ++-
>   2 files changed, 450 insertions(+), 67 deletions(-)
> 

So the first question that comes to mind is: Why can't the gobi plugin drive 
this device?  One thing I see that is different is that this plugin uses 
QMI_DMS_OPER_MODE_PERSIST_LOW_POWER instead of QMI_DMS_OPER_MODE_LOW_POWER.  You 
also skip / hard code a few things, but that can be special cased by introducing 
a new attribute or two, similar to 'AlwaysOnline'.

On the upside, you should might get multi-context support and MTU handling for free.

<snip>

> +static void get_oper_mode_cb(struct qmi_result *result, void *user_data)
>   {
>   	struct ofono_modem *modem = user_data;
> +	struct droid_data *data = ofono_modem_get_data(modem);
> +	struct qmi_param *param;
> +	uint8_t mode;
>   
>   	DBG("");
>   
> -	if (ok)
> +	if (qmi_result_set_error(result, NULL)) {
> +		shutdown_device(modem);
> +		return;
> +	}
> +
> +	if (!qmi_result_get_uint8(result, QMI_DMS_RESULT_OPER_MODE, &mode)) {
> +		shutdown_device(modem);
> +		return;
> +	}

These shutdown_device(); return; lines might be better served by a goto/label.

> +
> +	data->oper_mode = mode;
> +
> +	switch (data->oper_mode) {
> +	case QMI_DMS_OPER_MODE_ONLINE:
> +		param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
> +					QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
> +		if (!param) {
> +			shutdown_device(modem);
> +			return;
> +		}

ditto

> +
> +		if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
> +					power_reset_cb, modem, NULL) > 0)
> +			return;
> +
> +		shutdown_device(modem);
> +		break;

ditto

> +	default:
>   		ofono_modem_set_powered(modem, TRUE);
> +		break;
> +	}
>   }
>   

<snip>

> -static int droid_enable(struct ofono_modem *modem)
> +static void get_caps_cb(struct qmi_result *result, void *user_data)
>   {
> -	GAtChat *chat;
> +	struct ofono_modem *modem = user_data;
> +	struct droid_data *data = ofono_modem_get_data(modem);
> +	const struct qmi_dms_device_caps *caps;
> +	uint16_t len;
> +	uint8_t i;
>   
>   	DBG("");
>   
> -	chat = at_util_open_device(modem, "Device", droid_debug, "", NULL);
> -	ofono_modem_set_data(modem, chat);
> +	if (qmi_result_set_error(result, NULL))
> +		goto error;
>   
> -	/* ensure modem is in a known state; verbose on, echo/quiet off */
> -	g_at_chat_send(chat, "ATE0Q0V1", NULL, NULL, NULL, NULL);
> +	caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
> +	if (!caps)
> +		goto error;
>   
> -	/* power up modem */
> -	g_at_chat_send(chat, "AT+CFUN=1", NULL, cfun_set_on_cb, modem, NULL);
> +	DBG("service capabilities %d", caps->data_capa);
> +	DBG("sim supported %d", caps->sim_supported);
>   
> -	return 0;
> +	for (i = 0; i < caps->radio_if_count; i++)
> +		DBG("radio = %d", caps->radio_if[i]);
> +
> +	if (qmi_service_send(data->dms, QMI_DMS_GET_OPER_MODE, NULL,
> +				get_oper_mode_cb, modem, NULL) > 0)
> +		return;
> +
> +error:
> +	shutdown_device(modem);

Like you do here :)

>   }
>   

<snip>

>   
> -static int droid_disable(struct ofono_modem *modem)
> +static void create_dms_cb(struct qmi_service *service, void *user_data)
> +{
> +	struct ofono_modem *modem = user_data;
> +	struct droid_data *data = ofono_modem_get_data(modem);
> +	struct service_request *request = NULL;
> +
> +	DBG("");
> +
> +	if (!service)
> +		goto error;
> +
> +	data->dms = service;
> +	request = l_queue_peek_head(data->service_requests);
> +
> +	if (qmi_qmux_device_create_client(data->qmux, request->service_type,
> +						create_service_cb, modem, NULL))
> +		return;
> +
> +error:
> +	shutdown_device(modem);
> +}

How is this function different from create_service_cb()?  Could you just use 
that one instead by queuing up a DMS request on the service_requests queue?

<snip>

> +
> +static int droid_disable(struct ofono_modem *modem)
> +{
> +	struct droid_data *data = ofono_modem_get_data(modem);
> +	struct qmi_param *param;
> +
> +	DBG("%p", modem);
> +
> +	param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
> +				QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
> +	if (!param)
> +		return -ENOMEM;
> +
> +	if (qmi_service_send(data->dms, QMI_DMS_SET_OPER_MODE, param,
> +				power_disable_cb, modem, NULL) > 0)
> +		return -EINPROGRESS;

Leaking param here

>   
> -	if (sim)
> -		ofono_sim_inserted_notify(sim, TRUE);
> +	shutdown_device(modem);
> +
> +	return -EINPROGRESS;
> +}

<snip>

> +
> +/* Only some QMI features are usable, voicecall and sms are custom */

Not quite sure what this comment means?  I don't see any vendor customization 
for voicecalls or sms?  Maybe comment is stale?

> +static void droid_pre_sim(struct ofono_modem *modem)
> +{
> +	struct droid_data *data = ofono_modem_get_data(modem);
> +
> +	DBG("%p", modem);
> +
> +	ofono_devinfo_create(modem, 0, "qmimodem",
> +				qmi_service_clone(data->dms));
> +	ofono_sim_create(modem, 0, "qmimodem",
> +				qmi_service_clone(data->dms),
> +				qmi_service_clone(data->uim));
> +	ofono_voicecall_create(modem, 0, "qmimodem",
> +				qmi_service_clone(data->voice));
> +	ofono_location_reporting_create(modem, 0, "qmimodem",
> +					l_steal_ptr(data->pds));
>   }
>   
>   static void droid_post_sim(struct ofono_modem *modem)
>   {
> -	GAtChat *chat = ofono_modem_get_data(modem);
> +	struct droid_data *data = ofono_modem_get_data(modem);
>   	struct ofono_message_waiting *mw;
> +	struct ofono_gprs *gprs;
> +	struct ofono_gprs_context *gc;
> +	const char *interface;
>   
> -	DBG("");
> +	DBG("%p", modem);
> +
> +/*	ofono_phonebook_create(modem, 0, "qmimodem", data->qmux);*/

Remove?

> +	ofono_radio_settings_create(modem, 0, "qmimodem",
> +					qmi_service_clone(data->dms),
> +					qmi_service_clone(data->nas));
>   
> -	ofono_ussd_create(modem, 0, "atmodem", chat);
> -	ofono_call_forwarding_create(modem, 0, "atmodem", chat);
> -	ofono_call_settings_create(modem, 0, "atmodem", chat);
> -	ofono_netreg_create(modem, 0, "atmodem", chat);
> -	/*
> -	 * Droid 4 modem has problems with AT+CPUC?, avoid call meter for now.
> -	 */
> -	ofono_call_barring_create(modem, 0, "atmodem", chat);
> -	ofono_sms_create(modem, OFONO_VENDOR_DROID, "atmodem", chat);
> -	ofono_phonebook_create(modem, 0, "atmodem", chat);
> +	ofono_sms_create(modem, 0, "qmimodem",
> +				qmi_service_clone(data->wms));
>   
>   	mw = ofono_message_waiting_create(modem);
>   	if (mw)
>   		ofono_message_waiting_register(mw);
> +
> +	gprs = ofono_gprs_create(modem, 0, "qmimodem",
> +					qmi_service_clone(data->wds),
> +					qmi_service_clone(data->nas));
> +	if (!gprs) {
> +		ofono_warn("Unable to create gprs for: %s",
> +				ofono_modem_get_path(modem));
> +		return;
> +	}
> +
> +	gc = ofono_gprs_context_create(modem, 0, "qmimodem", -1,
> +					qmi_service_clone(data->wds_ip4),
> +					qmi_service_clone(data->wds_ip6));
> +	if (!gc) {
> +		ofono_warn("Unable to create gprs-context for: %s",
> +				ofono_modem_get_path(modem));
> +		return;
> +	}
> +
> +	ofono_gprs_add_context(gprs, gc);
> +	interface = ofono_modem_get_string(modem, "NetworkInterface");
> +	ofono_gprs_context_set_interface(gc, interface);
> +}
> +

<snip>

> diff --git a/plugins/udevng.c b/plugins/udevng.c
> index 320cd3a6..7777996d 100644
> --- a/plugins/udevng.c
> +++ b/plugins/udevng.c
> @@ -870,7 +870,9 @@ static gboolean setup_telitqmi(struct modem_info *modem)
>   
>   static gboolean setup_droid(struct modem_info *modem)
>   {
> -	const char *at = NULL;
> +	const struct device_info *qmi = NULL;
> +	const struct device_info *net = NULL;
> +
>   	GSList *list;
>   
>   	DBG("%s", modem->syspath);
> @@ -878,21 +880,29 @@ static gboolean setup_droid(struct modem_info *modem)
>   	for (list = modem->devices; list; list = list->next) {
>   		struct device_info *info = list->data;
>   		const char *subsystem =
> -			udev_device_get_subsystem(info->udev_device);
> -
> -		DBG("%s %s %s %s %s", info->devnode, info->interface,
> -				info->number, info->label, subsystem);
> +				udev_device_get_subsystem(info->udev_device);
> +		DBG("%s %s %s %s %s %s", info->devnode, info->interface,
> +						info->number, info->label,
> +						info->sysattr, subsystem);
>   
> -		if (g_strcmp0(info->interface, "255/255/255") == 0 &&
> -				g_strcmp0(info->number, "04") == 0) {
> -			at = info->devnode;
> +		if (g_strcmp0(subsystem, "usbmisc") == 0) {
> +			if (g_strcmp0(info->number, "05") == 0)
> +				qmi = info;
> +		} else if (g_strcmp0(subsystem, "net") == 0) {
> +			if (g_strcmp0(info->number, "05") == 0)
> +				net = info;
>   		}
>   	}
>   
> -	if (at == NULL)
> +	if (qmi == NULL || net == NULL)
> +		return FALSE;
> +
> +	DBG("qmi=%s net=%s", qmi->devnode, get_ifname(net));
> +
> +

No double empty lines please

> +	if (setup_qmi_qmux(modem, qmi, net) < 0)
>   		return FALSE;
>   
> -	ofono_modem_set_string(modem->modem, "Device", at);
>   	ofono_modem_set_driver(modem->modem, "droid");
>   
>   	return TRUE;

Regards,
-Denis