Hi,

I have a Huawei E398 dongle with a qmi interface, and i'm trying to make ofono use the qmi interface for the gprs-context atom. I have this working, but depending on how i unref the qmi_device in huawei_disable, ofono crashes or there's have a memory leak.

The first approach i've tried is the same as in the gobi plugin.
I call qmi_device_shutdown() from huawei_disable(), and unref the qmi_device from the callback.
The problem with this is: when i unplug the device ofono crashes. 
When the device is unplugged, first  huawei_disable() is called, then huawei_remove(), and
afterwards shutdown_cb() is called. At that time the ofono_modem struct is already freed, shutdown_cb() tries to access it and crashes. 

The second approach i've tried is to unref the qmi_device directly from huawei_disable.
With this approach, i see a memory leak of the qmi_service. 
qmi_gprs_context_remove() is being called from flush_atoms(). And i can see release_client() is being called. But the service_release_callback(), which should free the service, is never being called.
I think the request get's canceled because qmi_device_unref() is being called from huawei_disable(). The request is destroyed and the callback which should free the qmi_service is never called.

Does anyone have thoughts/ideas on how i can fix this?

Below are the changes to the huawei plugin to add qmi support (other patches are needed to make it work, but they're not relevant to this issue).

Thanks,
Cedric

diff --git a/plugins/huawei.c b/plugins/huawei.c
index 5d8875a..7003760 100644
--- a/plugins/huawei.c
+++ b/plugins/huawei.c
@@ -26,6 +26,8 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 #include <glib.h>
 #include <gatchat.h>
@@ -57,6 +59,10 @@
 #include <drivers/atmodem/atutil.h>
 #include <drivers/atmodem/vendor.h>
 
+#include <drivers/qmimodem/qmi.h>
+#include <drivers/qmimodem/dms.h>
+#include <drivers/qmimodem/util.h>
+
 static const char *none_prefix[] = { NULL };
 static const char *gcap_prefix[] = { "+GCAP:", NULL };
 static const char *rfswitch_prefix[] = { "^RFSWITCH:", NULL };
@@ -78,6 +84,7 @@ enum {
 struct huawei_data {
        GAtChat *modem;
        GAtChat *pcui;
+       struct qmi_device *device;
        gboolean have_sim;
        int sim_state;
        guint sysinfo_poll_source;
@@ -120,6 +127,7 @@ static void huawei_remove(struct ofono_modem *modem)
 
        /* Cleanup after hot-unplug */
        g_at_chat_unref(data->pcui);
+       qmi_device_unref(data->device);
 
        g_free(data);
 }
@@ -577,6 +585,36 @@ static GAtChat *open_device(struct ofono_modem *modem,
        return chat;
 }
 
+static struct qmi_device *open_qmi_device(struct ofono_modem *modem,
+                                       const char *key, char *debug)
+
+{
+       const char *device;
+       struct qmi_device *qmi_device;
+       int fd;
+
+       device = ofono_modem_get_string(modem, key);
+       if (device == NULL)
+               return NULL;
+       DBG("%s %s", key, device);
+
+       fd = open(device, O_RDWR | O_NONBLOCK | O_CLOEXEC);
+       if (fd < 0)
+               return NULL;
+
+       qmi_device = qmi_device_new(fd);
+       if (!qmi_device) {
+               close(fd);
+               return NULL;
+       }
+       if (getenv("OFONO_QMI_DEBUG"))
+               qmi_device_set_debug(qmi_device, huawei_debug, debug);
+
+       qmi_device_set_close_on_unref(qmi_device, true);
+       return qmi_device;
+
+}
+
 static int huawei_enable(struct ofono_modem *modem)
 {
        struct huawei_data *data = ofono_modem_get_data(modem);
@@ -594,6 +632,8 @@ static int huawei_enable(struct ofono_modem *modem)
                return -EIO;
        }
 
+       data->device = open_qmi_device(modem, "Device", "QMI: ");
+
        g_at_chat_set_slave(data->modem, data->pcui);
 
        g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
@@ -622,6 +662,17 @@ static void cfun_disable(gboolean ok, GAtResult *result, gpointer user_data)
                ofono_modem_set_powered(modem, FALSE);
 }
 
+static void qmi_shutdown_cb(void *user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct huawei_data *data = ofono_modem_get_data(modem);
+
+       DBG("");
+
+       qmi_device_unref(data->device);
+       data->device = NULL;
+}
+
 static int huawei_disable(struct ofono_modem *modem)
 {
        struct huawei_data *data = ofono_modem_get_data(modem);
@@ -646,9 +697,14 @@ static int huawei_disable(struct ofono_modem *modem)
                data->online_cbd = NULL;
        }
 
+       if (data->device) {
+               qmi_device_unref(data->device);
+               data->device = NULL;
+       //      qmi_device_shutdown(data->device, qmi_shutdown_cb, modem, NULL);
+       }
+
        g_at_chat_send(data->pcui, "AT+CFUN=0", none_prefix,
                                        cfun_disable, modem, NULL);
-
        return -EINPROGRESS;
 }
 
@@ -804,6 +860,7 @@ static void huawei_post_sim(struct ofono_modem *modem)
 {
        struct huawei_data *data = ofono_modem_get_data(modem);
 
+
        DBG("%p", modem);
 
        if (data->have_voice == TRUE) {
@@ -822,11 +879,15 @@ static void huawei_post_sim(struct ofono_modem *modem)
 
                ofono_sms_create(modem, OFONO_VENDOR_HUAWEI,
                                                "atmodem", data->pcui);
-
                gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI,
                                                "atmodem", data->pcui);
-               gc = ofono_gprs_context_create(modem, 0,
+               if (data->device == NULL) {
+                       gc = ofono_gprs_context_create(modem, OFONO_VENDOR_HUAWEI,
                                                "atmodem", data->modem);
+               } else {
+                       gc = ofono_gprs_context_create(modem, 0,
+                                               "qmimodem", data->device);
+               }
 
                if (gprs && gc)
                        ofono_gprs_add_context(gprs, gc);