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

Ivaylo Dimitrov <[email protected]> Fri, 25 Jul 2025 08:45:01 +0300
Newsgroups dev.linux.lists.ofono
Message-ID <[email protected]>
Hi Denis,

On 25.07.25 г. 1:25 ч., Denis Kenzior wrote:
> 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?  

Now you asked, I am wondering too :). For sure gobi was not working in 
an year or two when I tried it back then.

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'.
> 

I wonder if QMI_DMS_OPER_MODE_PERSIST_LOW_POWER is not the proper state 
for all QMI modems when disabled. After all we don't want modem to auto 
wake-up. Besides droid, I have one quectel here that is supported by 
gobi, will test with it.

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

nice.

Ok, will try to make it work with gobi, if it does not, will re-send the 
current one with the requested fixes, otherwise will send a patch for gobi.


Thanks,
Ivo

> <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