summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/qmimodem/qmi.c130
1 files changed, 96 insertions, 34 deletions
diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c
index fc4534a2..e66e8d8f 100644
--- a/drivers/qmimodem/qmi.c
+++ b/drivers/qmimodem/qmi.c
@@ -260,6 +260,10 @@ static gboolean __service_compare_shared(gpointer key, gpointer value,
struct qmi_service *service = value;
uint8_t type = GPOINTER_TO_UINT(user_data);
+ /* ignore those that are in process of creation */
+ if (GPOINTER_TO_UINT(key) & 0x80000000)
+ return FALSE;
+
if (service->type == type)
return TRUE;
@@ -736,6 +740,10 @@ static void service_notify(gpointer key, gpointer value, gpointer user_data)
struct qmi_result *result = user_data;
GList *list;
+ /* ignore those that are in process of creation */
+ if (GPOINTER_TO_UINT(key) & 0x80000000)
+ return;
+
for (list = g_list_first(service->notify_list); list;
list = g_list_next(list)) {
struct qmi_notify *notify = list->data;
@@ -1967,12 +1975,58 @@ static void service_create_data_free(gpointer user_data)
g_free(data);
}
+struct service_create_shared_data {
+ struct discovery super;
+ struct qmi_service *service;
+ struct qmi_device *device;
+ qmi_create_func_t func;
+ void *user_data;
+ qmi_destroy_func_t destroy;
+ guint timeout;
+};
+
+static gboolean service_create_shared_reply(gpointer user_data)
+{
+ struct service_create_shared_data *data = user_data;
+
+ data->timeout = 0;
+ data->func(data->service, data->user_data);
+
+ __qmi_device_discovery_complete(data->device, &data->super);
+
+ return FALSE;
+}
+
+static void service_create_shared_pending_reply(struct qmi_device *device,
+ unsigned int type,
+ struct qmi_service *service)
+{
+ gpointer key = GUINT_TO_POINTER(type | 0x80000000);
+ GList **shared = g_hash_table_lookup(device->service_list, key);
+ GList *l;
+
+ g_hash_table_steal(device->service_list, key);
+
+ for (l = *shared; l; l = l->next) {
+ struct service_create_shared_data *shared_data = l->data;
+
+ shared_data->service = qmi_service_ref(service);
+ shared_data->timeout = g_timeout_add(
+ 0, service_create_shared_reply, shared_data);
+ }
+
+ g_list_free(*shared);
+ g_free(shared);
+}
+
static gboolean service_create_reply(gpointer user_data)
{
struct service_create_data *data = user_data;
struct qmi_device *device = data->device;
struct qmi_request *req;
+ service_create_shared_pending_reply(device, data->type, NULL);
+
/* remove request from queues */
req = find_control_request(device, data->tid);
@@ -2039,6 +2093,8 @@ static void service_create_callback(uint16_t message, uint16_t length,
GUINT_TO_POINTER(hash_id), service);
done:
+ service_create_shared_pending_reply(device, data->type, service);
+
data->func(service, data->user_data);
qmi_service_unref(service);
@@ -2052,15 +2108,23 @@ static bool service_create(struct qmi_device *device,
struct service_create_data *data;
unsigned char client_req[] = { 0x01, 0x01, 0x00, type };
struct qmi_request *req;
+ GList **shared;
+ unsigned int type_val = type;
int i;
- data = g_try_new0(struct service_create_data, 1);
- if (!data)
+ if (!device->version_list)
return false;
- if (!device->version_list)
+ shared = g_try_new0(GList *, 1);
+ if (!shared)
return false;
+ data = g_try_new0(struct service_create_data, 1);
+ if (!data) {
+ g_free(shared);
+ return false;
+ }
+
data->super.destroy = service_create_data_free;
data->device = device;
data->type = type;
@@ -2088,19 +2152,13 @@ static bool service_create(struct qmi_device *device,
__qmi_device_discovery_started(device, &data->super);
+ /* Mark service creation as pending */
+ g_hash_table_insert(device->service_list,
+ GUINT_TO_POINTER(type_val | 0x80000000), shared);
+
return true;
}
-struct service_create_shared_data {
- struct discovery super;
- struct qmi_service *service;
- struct qmi_device *device;
- qmi_create_func_t func;
- void *user_data;
- qmi_destroy_func_t destroy;
- guint timeout;
-};
-
static void service_create_shared_data_free(gpointer user_data)
{
struct service_create_shared_data *data = user_data;
@@ -2118,23 +2176,11 @@ static void service_create_shared_data_free(gpointer user_data)
g_free(data);
}
-static gboolean service_create_shared_reply(gpointer user_data)
+bool qmi_service_create_shared(struct qmi_device *device, uint8_t type,
+ qmi_create_func_t func, void *user_data,
+ qmi_destroy_func_t destroy)
{
- struct service_create_shared_data *data = user_data;
-
- data->timeout = 0;
- data->func(data->service, data->user_data);
-
- __qmi_device_discovery_complete(data->device, &data->super);
-
- return FALSE;
-}
-
-bool qmi_service_create_shared(struct qmi_device *device,
- uint8_t type, qmi_create_func_t func,
- void *user_data, qmi_destroy_func_t destroy)
-{
- struct qmi_service *service;
+ gpointer service;
unsigned int type_val = type;
if (!device || !func)
@@ -2143,8 +2189,14 @@ bool qmi_service_create_shared(struct qmi_device *device,
if (type == QMI_SERVICE_CONTROL)
return false;
- service = g_hash_table_find(device->service_list,
+ service = g_hash_table_lookup(device->service_list,
+ GUINT_TO_POINTER(type_val | 0x80000000));
+ if (!service) {
+ service = g_hash_table_find(device->service_list,
__service_compare_shared, GUINT_TO_POINTER(type_val));
+ } else
+ type_val |= 0x80000000;
+
if (service) {
struct service_create_shared_data *data;
@@ -2153,17 +2205,27 @@ bool qmi_service_create_shared(struct qmi_device *device,
return false;
data->super.destroy = service_create_shared_data_free;
- data->service = qmi_service_ref(service);
data->device = device;
data->func = func;
data->user_data = user_data;
data->destroy = destroy;
- data->timeout = g_timeout_add(0,
- service_create_shared_reply, data);
+ if (!data)
+ return false;
+
+ if (!(type_val & 0x80000000)) {
+ data->service = qmi_service_ref(service);
+ data->timeout = g_timeout_add(
+ 0, service_create_shared_reply, data);
+ } else {
+ GList **l = service;
+
+ *l = g_list_prepend(*l, data);
+ }
+
__qmi_device_discovery_started(device, &data->super);
- return 0;
+ return true;
}
return service_create(device, type, func, user_data, destroy);