3 * oFono - Open Source Telephony
5 * Copyright (C) 2011-2012 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39 typedef void (*qmi_message_func_t)(uint16_t message, uint16_t length,
40 const void *buffer, void *user_data);
50 GQueue *control_queue;
51 GQueue *service_queue;
52 uint8_t next_control_tid;
53 uint16_t next_service_tid;
54 qmi_debug_func_t debug_func;
56 uint16_t control_major;
57 uint16_t control_minor;
59 struct qmi_version *version_list;
60 uint8_t version_count;
61 GHashTable *service_list;
62 unsigned int release_users;
67 struct qmi_device *device;
73 uint16_t next_notify_id;
95 qmi_message_func_t callback;
102 qmi_result_func_t callback;
104 qmi_destroy_func_t destroy;
108 uint8_t frame; /* Always 0x01 */
109 uint16_t length; /* Packet size without frame byte */
110 uint8_t flags; /* Either 0x00 or 0x80 */
111 uint8_t service; /* Service type (0x00 for control) */
112 uint8_t client; /* Client identifier (0x00 for control) */
113 } __attribute__ ((packed));
114 #define QMI_MUX_HDR_SIZE 6
116 struct qmi_control_hdr {
117 uint8_t type; /* Bit 1 = response, Bit 2 = indication */
118 uint8_t transaction; /* Transaction identifier */
119 } __attribute__ ((packed));
120 #define QMI_CONTROL_HDR_SIZE 2
122 struct qmi_service_hdr {
123 uint8_t type; /* Bit 2 = response, Bit 3 = indication */
124 uint16_t transaction; /* Transaction identifier */
125 } __attribute__ ((packed));
126 #define QMI_SERVICE_HDR_SIZE 3
128 struct qmi_message_hdr {
129 uint16_t message; /* Message identifier */
130 uint16_t length; /* Message size without header */
132 } __attribute__ ((packed));
133 #define QMI_MESSAGE_HDR_SIZE 4
139 } __attribute__ ((packed));
140 #define QMI_TLV_HDR_SIZE 3
142 void qmi_free(void *ptr)
147 static struct qmi_request *__request_alloc(uint8_t service,
148 uint8_t client, uint16_t message,
149 uint16_t headroom, const void *data,
150 uint16_t length, qmi_message_func_t func,
151 void *user_data, void **head)
153 struct qmi_request *req;
154 struct qmi_mux_hdr *hdr;
155 struct qmi_message_hdr *msg;
157 req = g_try_new0(struct qmi_request, 1);
161 req->len = QMI_MUX_HDR_SIZE + headroom + QMI_MESSAGE_HDR_SIZE + length;
163 req->buf = g_try_malloc(req->len);
169 req->client = client;
174 hdr->length = GUINT16_TO_LE(req->len - 1);
176 hdr->service = service;
177 hdr->client = client;
179 msg = req->buf + QMI_MUX_HDR_SIZE + headroom;
181 msg->message = GUINT16_TO_LE(message);
182 msg->length = GUINT16_TO_LE(length);
184 if (data && length > 0)
185 memcpy(req->buf + QMI_MUX_HDR_SIZE + headroom +
186 QMI_MESSAGE_HDR_SIZE, data, length);
188 req->callback = func;
189 req->user_data = user_data;
191 *head = req->buf + QMI_MUX_HDR_SIZE;
196 static void __request_free(gpointer data, gpointer user_data)
198 struct qmi_request *req = data;
204 static gint __request_compare(gconstpointer a, gconstpointer b)
206 const struct qmi_request *req = a;
207 uint16_t tid = GPOINTER_TO_UINT(b);
209 return req->tid - tid;
212 static void __notify_free(gpointer data, gpointer user_data)
214 struct qmi_notify *notify = data;
217 notify->destroy(notify->user_data);
222 static gint __notify_compare(gconstpointer a, gconstpointer b)
224 const struct qmi_notify *notify = a;
225 uint16_t id = GPOINTER_TO_UINT(b);
227 return notify->id - id;
230 static gboolean __service_compare_shared(gpointer key, gpointer value,
233 struct qmi_service *service = value;
234 uint8_t type = GPOINTER_TO_UINT(user_data);
236 if (!service->shared)
239 if (service->type == type)
245 static void __hexdump(const char dir, const unsigned char *buf, size_t len,
246 qmi_debug_func_t function, void *user_data)
248 static const char hexdigits[] = "0123456789abcdef";
252 if (!function || !len)
257 for (i = 0; i < len; i++) {
258 str[((i % 16) * 3) + 1] = ' ';
259 str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
260 str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
261 str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
263 if ((i + 1) % 16 == 0) {
267 function(str, user_data);
274 for (j = (i % 16); j < 16; j++) {
275 str[(j * 3) + 1] = ' ';
276 str[(j * 3) + 2] = ' ';
277 str[(j * 3) + 3] = ' ';
283 function(str, user_data);
287 static const char *__service_type_to_string(uint8_t type)
290 case QMI_SERVICE_CONTROL:
292 case QMI_SERVICE_WDS:
294 case QMI_SERVICE_DMS:
296 case QMI_SERVICE_NAS:
298 case QMI_SERVICE_QOS:
300 case QMI_SERVICE_WMS:
302 case QMI_SERVICE_PDS:
304 case QMI_SERVICE_AUTH:
308 case QMI_SERVICE_VOICE:
310 case QMI_SERVICE_CAT:
312 case QMI_SERVICE_UIM:
314 case QMI_SERVICE_PBM:
316 case QMI_SERVICE_RMTFS:
318 case QMI_SERVICE_LOC:
320 case QMI_SERVICE_SAR:
322 case QMI_SERVICE_CSD:
324 case QMI_SERVICE_EFS:
328 case QMI_SERVICE_TMD:
330 case QMI_SERVICE_PDC:
332 case QMI_SERVICE_CAT_OLD:
334 case QMI_SERVICE_RMS:
336 case QMI_SERVICE_OMA:
343 static const struct {
346 } __error_table[] = {
348 { 0x0001, "MALFORMED_MSG" },
349 { 0x0002, "NO_MEMORY" },
350 { 0x0003, "INTERNAL" },
351 { 0x0004, "ABORTED" },
352 { 0x0005, "CLIENT_IDS_EXHAUSTED" },
353 { 0x0006, "UNABORTABLE_TRANSACTION" },
354 { 0x0007, "INVALID_CLIENT_ID" },
355 { 0x0008, "NO_THRESHOLDS" },
356 { 0x0009, "INVALID_HANDLE" },
357 { 0x000a, "INVALID_PROFILE" },
358 { 0x000b, "INVALID_PINID" },
359 { 0x000c, "INCORRECT_PIN" },
360 { 0x000d, "NO_NETWORK_FOUND" },
361 { 0x000e, "CALL_FAILED" },
362 { 0x000f, "OUT_OF_CALL" },
363 { 0x0010, "NOT_PROVISIONED" },
364 { 0x0011, "MISSING_ARG" },
365 { 0x0013, "ARG_TOO_LONG" },
366 { 0x0016, "INVALID_TX_ID" },
367 { 0x0017, "DEVICE_IN_USE" },
368 { 0x0018, "OP_NETWORK_UNSUPPORTED" },
369 { 0x0019, "OP_DEVICE_UNSUPPORTED" },
370 { 0x001a, "NO_EFFECT" },
371 { 0x001b, "NO_FREE_PROFILE" },
372 { 0x001c, "INVALID_PDP_TYPE" },
373 { 0x001d, "INVALID_TECH_PREF" },
374 { 0x001e, "INVALID_PROFILE_TYPE" },
375 { 0x001f, "INVALID_SERVICE_TYPE" },
376 { 0x0020, "INVALID_REGISTER_ACTION" },
377 { 0x0021, "INVALID_PS_ATTACH_ACTION" },
378 { 0x0022, "AUTHENTICATION_FAILED" },
379 { 0x0023, "PIN_BLOCKED" },
380 { 0x0024, "PIN_PERM_BLOCKED" },
381 { 0x0025, "UIM_NOT_INITIALIZED" },
382 { 0x0026, "MAX_QOS_REQUESTS_IN_USE" },
383 { 0x0027, "INCORRECT_FLOW_FILTER" },
384 { 0x0028, "NETWORK_QOS_UNAWARE" },
385 { 0x0029, "INVALID_QOS_ID/INVALID_ID" },
386 { 0x002a, "REQUESTED_NUM_UNSUPPORTED" },
387 { 0x002b, "INTERFACE_NOT_FOUND" },
388 { 0x002c, "FLOW_SUSPENDED" },
389 { 0x002d, "INVALID_DATA_FORMAT" },
390 { 0x002e, "GENERAL" },
391 { 0x002f, "UNKNOWN" },
392 { 0x0030, "INVALID_ARG" },
393 { 0x0031, "INVALID_INDEX" },
394 { 0x0032, "NO_ENTRY" },
395 { 0x0033, "DEVICE_STORAGE_FULL" },
396 { 0x0034, "DEVICE_NOT_READY" },
397 { 0x0035, "NETWORK_NOT_READY" },
398 { 0x0036, "CAUSE_CODE" },
399 { 0x0037, "MESSAGE_NOT_SENT" },
400 { 0x0038, "MESSAGE_DELIVERY_FAILURE" },
401 { 0x0039, "INVALID_MESSAGE_ID" },
402 { 0x003a, "ENCODING" },
403 { 0x003b, "AUTHENTICATION_LOCK" },
404 { 0x003c, "INVALID_TRANSACTION" },
405 { 0x0041, "SESSION_INACTIVE" },
406 { 0x0042, "SESSION_INVALID" },
407 { 0x0043, "SESSION_OWNERSHIP" },
408 { 0x0044, "INSUFFICIENT_RESOURCES" },
409 { 0x0045, "DISABLED" },
410 { 0x0046, "INVALID_OPERATION" },
411 { 0x0047, "INVALID_QMI_CMD" },
412 { 0x0048, "TPDU_TYPE" },
413 { 0x0049, "SMSC_ADDR" },
414 { 0x004a, "INFO_UNAVAILABLE" },
415 { 0x004b, "SEGMENT_TOO_LONG" },
416 { 0x004c, "SEGEMENT_ORDER" },
417 { 0x004d, "BUNDLING_NOT_SUPPORTED" },
418 { 0x004f, "POLICY_MISMATCH" },
419 { 0x0050, "SIM_FILE_NOT_FOUND" },
420 { 0x0051, "EXTENDED_INTERNAL" },
421 { 0x0052, "ACCESS_DENIED" },
422 { 0x0053, "HARDWARE_RESTRICTED" },
423 { 0x0054, "ACK_NOT_SENT" },
424 { 0x0055, "INJECT_TIMEOUT" },
428 static const char *__error_to_string(uint16_t error)
432 for (i = 0; __error_table[i].str; i++) {
433 if (__error_table[i].err == error)
434 return __error_table[i].str;
440 static void __debug_msg(const char dir, const void *buf, size_t len,
441 qmi_debug_func_t function, void *user_data)
443 const struct qmi_mux_hdr *hdr;
444 const struct qmi_message_hdr *msg;
448 char strbuf[72 + 16], *str;
449 bool pending_print = false;
451 if (!function || !len)
457 service = __service_type_to_string(hdr->service);
459 str += sprintf(str, "%c %s", dir, service);
461 str += sprintf(str, "%c %d", dir, hdr->service);
463 if (hdr->service == QMI_SERVICE_CONTROL) {
464 const struct qmi_control_hdr *ctl;
467 ctl = buf + QMI_MUX_HDR_SIZE;
468 msg = buf + QMI_MUX_HDR_SIZE + QMI_CONTROL_HDR_SIZE;
469 ptr = buf + QMI_MUX_HDR_SIZE + QMI_CONTROL_HDR_SIZE +
470 QMI_MESSAGE_HDR_SIZE;
487 str += sprintf(str, "%s msg=%d len=%d", type,
488 GUINT16_FROM_LE(msg->message),
489 GUINT16_FROM_LE(msg->length));
491 str += sprintf(str, " [client=%d,type=%d,tid=%d,len=%d]",
492 hdr->client, ctl->type,
494 GUINT16_FROM_LE(hdr->length));
496 const struct qmi_service_hdr *srv;
499 srv = buf + QMI_MUX_HDR_SIZE;
500 msg = buf + QMI_MUX_HDR_SIZE + QMI_SERVICE_HDR_SIZE;
501 ptr = buf + QMI_MUX_HDR_SIZE + QMI_SERVICE_HDR_SIZE +
502 QMI_MESSAGE_HDR_SIZE;
519 str += sprintf(str, "%s msg=%d len=%d", type,
520 GUINT16_FROM_LE(msg->message),
521 GUINT16_FROM_LE(msg->length));
523 str += sprintf(str, " [client=%d,type=%d,tid=%d,len=%d]",
524 hdr->client, srv->type,
525 GUINT16_FROM_LE(srv->transaction),
526 GUINT16_FROM_LE(hdr->length));
529 function(strbuf, user_data);
535 str += sprintf(str, " ");
538 while (offset + QMI_TLV_HDR_SIZE < GUINT16_FROM_LE(msg->length)) {
539 const struct qmi_tlv_hdr *tlv = ptr + offset;
540 uint16_t tlv_length = GUINT16_FROM_LE(tlv->length);
542 if (tlv->type == 0x02 && tlv_length == QMI_RESULT_CODE_SIZE) {
543 const struct qmi_result_code *result = ptr + offset +
545 uint16_t error = GUINT16_FROM_LE(result->error);
546 const char *error_str;
548 error_str = __error_to_string(error);
550 str += sprintf(str, " {type=%d,error=%s}",
551 tlv->type, error_str);
553 str += sprintf(str, " {type=%d,error=%d}",
556 str += sprintf(str, " {type=%d,len=%d}", tlv->type,
560 if (str - strbuf > 60) {
561 function(strbuf, user_data);
564 str += sprintf(str, " ");
566 pending_print = false;
568 pending_print = true;
570 offset += QMI_TLV_HDR_SIZE + tlv_length;
574 function(strbuf, user_data);
577 static void __debug_device(struct qmi_device *device,
578 const char *format, ...)
580 char strbuf[72 + 16];
583 if (!device->debug_func)
586 va_start(ap, format);
587 vsnprintf(strbuf, sizeof(strbuf), format, ap);
590 device->debug_func(strbuf, device->debug_data);
593 static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
596 struct qmi_device *device = user_data;
597 struct qmi_mux_hdr *hdr;
598 struct qmi_request *req;
599 ssize_t bytes_written;
601 req = g_queue_pop_head(device->req_queue);
605 bytes_written = write(device->fd, req->buf, req->len);
606 if (bytes_written < 0)
609 __hexdump('>', req->buf, bytes_written,
610 device->debug_func, device->debug_data);
612 __debug_msg(' ', req->buf, bytes_written,
613 device->debug_func, device->debug_data);
617 if (hdr->service == QMI_SERVICE_CONTROL)
618 g_queue_push_tail(device->control_queue, req);
620 g_queue_push_tail(device->service_queue, req);
625 if (g_queue_get_length(device->req_queue) > 0)
631 static void write_watch_destroy(gpointer user_data)
633 struct qmi_device *device = user_data;
635 device->write_watch = 0;
638 static void wakeup_writer(struct qmi_device *device)
640 if (device->write_watch > 0)
643 device->write_watch = g_io_add_watch_full(device->io, G_PRIORITY_HIGH,
644 G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
645 can_write_data, device, write_watch_destroy);
648 static void __request_submit(struct qmi_device *device,
649 struct qmi_request *req, uint16_t transaction)
651 req->tid = transaction;
653 g_queue_push_tail(device->req_queue, req);
655 wakeup_writer(device);
658 static void service_notify(gpointer key, gpointer value, gpointer user_data)
660 struct qmi_service *service = value;
661 struct qmi_result *result = user_data;
664 for (list = g_list_first(service->notify_list); list;
665 list = g_list_next(list)) {
666 struct qmi_notify *notify = list->data;
668 if (notify->message == result->message)
669 notify->callback(result, notify->user_data);
673 static void handle_indication(struct qmi_device *device,
674 uint8_t service_type, uint8_t client_id,
675 uint16_t message, uint16_t length, const void *data)
677 struct qmi_service *service;
678 struct qmi_result result;
679 unsigned int hash_id;
681 if (service_type == QMI_SERVICE_CONTROL)
686 result.message = message;
688 result.length = length;
690 if (client_id == 0xff) {
691 g_hash_table_foreach(device->service_list,
692 service_notify, &result);
696 hash_id = service_type | (client_id << 8);
698 service = g_hash_table_lookup(device->service_list,
699 GUINT_TO_POINTER(hash_id));
703 service_notify(NULL, service, &result);
706 static void handle_packet(struct qmi_device *device,
707 const struct qmi_mux_hdr *hdr, const void *buf)
709 struct qmi_request *req;
710 uint16_t message, length;
713 if (hdr->service == QMI_SERVICE_CONTROL) {
714 const struct qmi_control_hdr *control = buf;
715 const struct qmi_message_hdr *msg;
719 /* Ignore control messages with client identifier */
720 if (hdr->client != 0x00)
723 msg = buf + QMI_CONTROL_HDR_SIZE;
725 message = GUINT16_FROM_LE(msg->message);
726 length = GUINT16_FROM_LE(msg->length);
728 data = buf + QMI_CONTROL_HDR_SIZE + QMI_MESSAGE_HDR_SIZE;
730 tid = control->transaction;
732 if (control->type == 0x02 && control->transaction == 0x00) {
733 handle_indication(device, hdr->service, hdr->client,
734 message, length, data);
738 list = g_queue_find_custom(device->control_queue,
739 GUINT_TO_POINTER(tid), __request_compare);
745 g_queue_delete_link(device->control_queue, list);
747 const struct qmi_service_hdr *service = buf;
748 const struct qmi_message_hdr *msg;
752 msg = buf + QMI_SERVICE_HDR_SIZE;
754 message = GUINT16_FROM_LE(msg->message);
755 length = GUINT16_FROM_LE(msg->length);
757 data = buf + QMI_SERVICE_HDR_SIZE + QMI_MESSAGE_HDR_SIZE;
759 tid = GUINT16_FROM_LE(service->transaction);
761 if (service->type == 0x04 && tid == 0x0000) {
762 handle_indication(device, hdr->service, hdr->client,
763 message, length, data);
767 list = g_queue_find_custom(device->service_queue,
768 GUINT_TO_POINTER(tid), __request_compare);
774 g_queue_delete_link(device->service_queue, list);
778 req->callback(message, length, data, req->user_data);
780 __request_free(req, NULL);
783 static gboolean received_data(GIOChannel *channel, GIOCondition cond,
786 struct qmi_device *device = user_data;
787 struct qmi_mux_hdr *hdr;
788 unsigned char buf[2048];
792 if (cond & G_IO_NVAL)
795 bytes_read = read(device->fd, buf, sizeof(buf));
799 __hexdump('<', buf, bytes_read,
800 device->debug_func, device->debug_data);
804 while (offset < bytes_read) {
807 /* Check if QMI mux header fits into packet */
808 if (bytes_read - offset < QMI_MUX_HDR_SIZE)
811 hdr = (void *) (buf + offset);
813 /* Check for fixed frame and flags value */
814 if (hdr->frame != 0x01 || hdr->flags != 0x80)
817 len = GUINT16_FROM_LE(hdr->length) + 1;
819 /* Check that packet size matches frame size */
820 if (bytes_read - offset < len)
823 __debug_msg(' ', buf + offset, len,
824 device->debug_func, device->debug_data);
826 handle_packet(device, hdr, buf + offset + QMI_MUX_HDR_SIZE);
834 static void read_watch_destroy(gpointer user_data)
836 struct qmi_device *device = user_data;
838 device->read_watch = 0;
841 static void service_destroy(gpointer data)
843 struct qmi_service *service = data;
845 if (!service->device)
848 service->device = NULL;
851 struct qmi_device *qmi_device_new(int fd)
853 struct qmi_device *device;
856 device = g_try_new0(struct qmi_device, 1);
860 __debug_device(device, "device %p new", device);
862 device->ref_count = 1;
865 device->close_on_unref = false;
867 flags = fcntl(device->fd, F_GETFL, NULL);
873 if (!(flags & O_NONBLOCK)) {
874 if (fcntl(device->fd, F_SETFL, flags | O_NONBLOCK) < 0) {
880 device->io = g_io_channel_unix_new(device->fd);
882 g_io_channel_set_encoding(device->io, NULL, NULL);
883 g_io_channel_set_buffered(device->io, FALSE);
885 device->read_watch = g_io_add_watch_full(device->io, G_PRIORITY_DEFAULT,
886 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
887 received_data, device, read_watch_destroy);
889 g_io_channel_unref(device->io);
891 device->req_queue = g_queue_new();
892 device->control_queue = g_queue_new();
893 device->service_queue = g_queue_new();
895 device->service_list = g_hash_table_new_full(g_direct_hash,
896 g_direct_equal, NULL, service_destroy);
901 struct qmi_device *qmi_device_ref(struct qmi_device *device)
906 __sync_fetch_and_add(&device->ref_count, 1);
911 void qmi_device_unref(struct qmi_device *device)
916 if (__sync_sub_and_fetch(&device->ref_count, 1))
919 __debug_device(device, "device %p free", device);
921 g_queue_foreach(device->control_queue, __request_free, NULL);
922 g_queue_free(device->control_queue);
924 g_queue_foreach(device->service_queue, __request_free, NULL);
925 g_queue_free(device->service_queue);
927 g_queue_foreach(device->req_queue, __request_free, NULL);
928 g_queue_free(device->req_queue);
930 if (device->write_watch > 0)
931 g_source_remove(device->write_watch);
933 if (device->read_watch > 0)
934 g_source_remove(device->read_watch);
936 if (device->close_on_unref)
939 g_hash_table_destroy(device->service_list);
941 g_free(device->version_str);
942 g_free(device->version_list);
947 void qmi_device_set_debug(struct qmi_device *device,
948 qmi_debug_func_t func, void *user_data)
953 device->debug_func = func;
954 device->debug_data = user_data;
957 void qmi_device_set_close_on_unref(struct qmi_device *device, bool do_close)
962 device->close_on_unref = do_close;
965 static const void *tlv_get(const void *data, uint16_t size,
966 uint8_t type, uint16_t *length)
968 const void *ptr = data;
971 while (len > QMI_TLV_HDR_SIZE) {
972 const struct qmi_tlv_hdr *tlv = ptr;
973 uint16_t tlv_length = GUINT16_FROM_LE(tlv->length);
975 if (tlv->type == type) {
977 *length = tlv_length;
979 return ptr + QMI_TLV_HDR_SIZE;
982 ptr += QMI_TLV_HDR_SIZE + tlv_length;
983 len -= QMI_TLV_HDR_SIZE + tlv_length;
989 struct discover_data {
990 struct qmi_device *device;
991 qmi_discover_func_t func;
993 qmi_destroy_func_t destroy;
997 static void discover_callback(uint16_t message, uint16_t length,
998 const void *buffer, void *user_data)
1000 struct discover_data *data = user_data;
1001 struct qmi_device *device = data->device;
1002 const struct qmi_result_code *result_code;
1003 const struct qmi_service_list *service_list;
1006 struct qmi_version *list;
1010 g_source_remove(data->timeout);
1015 result_code = tlv_get(buffer, length, 0x02, &len);
1019 if (len != QMI_RESULT_CODE_SIZE)
1022 service_list = tlv_get(buffer, length, 0x01, &len);
1026 if (len < QMI_SERVICE_LIST_SIZE)
1029 list = g_try_malloc(sizeof(struct qmi_version) * service_list->count);
1033 for (i = 0; i < service_list->count; i++) {
1035 GUINT16_FROM_LE(service_list->services[i].major);
1037 GUINT16_FROM_LE(service_list->services[i].minor);
1038 uint8_t type = service_list->services[i].type;
1039 const char *name = __service_type_to_string(type);
1041 if (type == QMI_SERVICE_CONTROL) {
1042 device->control_major = major;
1043 device->control_minor = minor;
1047 list[count].type = type;
1048 list[count].major = major;
1049 list[count].minor = minor;
1050 list[count].name = name;
1055 __debug_device(device, "found service [%s %d.%d]",
1056 name, major, minor);
1058 __debug_device(device, "found service [%d %d.%d]",
1059 type, major, minor);
1062 ptr = tlv_get(buffer, length, 0x10, &len);
1066 device->version_str = strndup(ptr + 1, *((uint8_t *) ptr));
1068 service_list = ptr + *((uint8_t *) ptr) + 1;
1070 for (i = 0; i < service_list->count; i++) {
1071 if (service_list->services[i].type == QMI_SERVICE_CONTROL)
1076 device->version_list = list;
1077 device->version_count = count;
1080 data->func(count, list, data->user_data);
1083 data->destroy(data->user_data);
1088 static gboolean discover_reply(gpointer user_data)
1090 struct discover_data *data = user_data;
1091 struct qmi_device *device = data->device;
1096 data->func(device->version_count,
1097 device->version_list, data->user_data);
1100 data->destroy(data->user_data);
1107 bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
1108 void *user_data, qmi_destroy_func_t destroy)
1110 struct discover_data *data;
1111 struct qmi_request *req;
1112 struct qmi_control_hdr *hdr;
1117 __debug_device(device, "device %p discover", device);
1119 data = g_try_new0(struct discover_data, 1);
1123 data->device = device;
1125 data->user_data = user_data;
1126 data->destroy = destroy;
1128 if (device->version_list) {
1129 g_timeout_add_seconds(0, discover_reply, data);
1133 req = __request_alloc(QMI_SERVICE_CONTROL, 0x00,
1134 QMI_CTL_GET_VERSION_INFO, QMI_CONTROL_HDR_SIZE,
1135 NULL, 0, discover_callback, data, (void **) &hdr);
1141 if (device->next_control_tid < 1)
1142 device->next_control_tid = 1;
1145 hdr->transaction = device->next_control_tid++;
1147 __request_submit(device, req, hdr->transaction);
1149 data->timeout = g_timeout_add_seconds(5, discover_reply, data);
1154 static void release_client(struct qmi_device *device,
1155 uint8_t type, uint8_t client_id,
1156 qmi_message_func_t func, void *user_data)
1158 unsigned char release_req[] = { 0x01, 0x02, 0x00, type, client_id };
1159 struct qmi_request *req;
1160 struct qmi_control_hdr *hdr;
1162 req = __request_alloc(QMI_SERVICE_CONTROL, 0x00,
1163 QMI_CTL_RELEASE_CLIENT_ID, QMI_CONTROL_HDR_SIZE,
1164 release_req, sizeof(release_req),
1165 func, user_data, (void **) &hdr);
1167 func(0x0000, 0x0000, NULL, user_data);
1171 if (device->next_control_tid < 1)
1172 device->next_control_tid = 1;
1175 hdr->transaction = device->next_control_tid++;
1177 __request_submit(device, req, hdr->transaction);
1180 struct shutdown_data {
1181 struct qmi_device *device;
1182 qmi_shutdown_func_t func;
1184 qmi_destroy_func_t destroy;
1187 static gboolean shutdown_reply(gpointer user_data)
1189 struct shutdown_data *data = user_data;
1192 data->func(data->user_data);
1199 static gboolean shutdown_timeout(gpointer user_data)
1201 struct shutdown_data *data = user_data;
1202 struct qmi_device *device = data->device;
1204 if (device->release_users > 0)
1207 return shutdown_reply(data);
1210 bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func,
1211 void *user_data, qmi_destroy_func_t destroy)
1213 struct shutdown_data *data;
1218 __debug_device(device, "device %p shutdown", device);
1220 data = g_try_new0(struct shutdown_data, 1);
1224 data->device = device;
1226 data->user_data = user_data;
1227 data->destroy = destroy;
1229 if (device->release_users > 0)
1230 g_timeout_add_seconds(0, shutdown_timeout, data);
1232 g_timeout_add_seconds(0, shutdown_reply, data);
1237 struct qmi_param *qmi_param_new(void)
1239 struct qmi_param *param;
1241 param = g_try_new0(struct qmi_param, 1);
1248 void qmi_param_free(struct qmi_param *param)
1253 g_free(param->data);
1257 bool qmi_param_append(struct qmi_param *param, uint8_t type,
1258 uint16_t length, const void *data)
1260 struct qmi_tlv_hdr *tlv;
1263 if (!param || !type)
1273 ptr = g_try_realloc(param->data,
1274 param->length + QMI_TLV_HDR_SIZE + length);
1276 ptr = g_try_malloc(QMI_TLV_HDR_SIZE + length);
1281 tlv = ptr + param->length;
1284 tlv->length = GUINT16_TO_LE(length);
1285 memcpy(tlv->value, data, length);
1288 param->length += QMI_TLV_HDR_SIZE + length;
1293 bool qmi_param_append_uint8(struct qmi_param *param, uint8_t type,
1296 unsigned char buf[1] = { value };
1298 return qmi_param_append(param, type, sizeof(buf), buf);
1301 bool qmi_param_append_uint16(struct qmi_param *param, uint8_t type,
1304 unsigned char buf[2] = { value & 0xff, (value & 0xff00) >> 8 };
1306 return qmi_param_append(param, type, sizeof(buf), buf);
1309 bool qmi_param_append_uint32(struct qmi_param *param, uint8_t type,
1312 unsigned char buf[4] = { value & 0xff, (value & 0xff00) >> 8,
1313 (value & 0xff0000) >> 16,
1314 (value & 0xff000000) >> 24 };
1316 return qmi_param_append(param, type, sizeof(buf), buf);
1319 struct qmi_param *qmi_param_new_uint8(uint8_t type, uint8_t value)
1321 struct qmi_param *param;
1323 param = qmi_param_new();
1327 if (!qmi_param_append_uint8(param, type, value)) {
1328 qmi_param_free(param);
1335 struct qmi_param *qmi_param_new_uint16(uint8_t type, uint16_t value)
1337 struct qmi_param *param;
1339 param = qmi_param_new();
1343 if (!qmi_param_append_uint16(param, type, value)) {
1344 qmi_param_free(param);
1351 struct qmi_param *qmi_param_new_uint32(uint8_t type, uint32_t value)
1353 struct qmi_param *param;
1355 param = qmi_param_new();
1359 if (!qmi_param_append_uint32(param, type, value)) {
1360 qmi_param_free(param);
1367 bool qmi_result_set_error(struct qmi_result *result, uint16_t *error)
1375 if (result->result == 0x0000)
1379 *error = result->error;
1384 const char *qmi_result_get_error(struct qmi_result *result)
1389 if (result->result == 0x0000)
1392 return __error_to_string(result->error);
1395 const void *qmi_result_get(struct qmi_result *result, uint8_t type,
1398 if (!result || !type)
1401 return tlv_get(result->data, result->length, type, length);
1404 char *qmi_result_get_string(struct qmi_result *result, uint8_t type)
1409 if (!result || !type)
1412 ptr = tlv_get(result->data, result->length, type, &len);
1416 return strndup(ptr, len);
1419 bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type,
1422 const unsigned char *ptr;
1425 if (!result || !type)
1428 ptr = tlv_get(result->data, result->length, type, &len);
1438 bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type,
1441 const unsigned char *ptr;
1444 if (!result || !type)
1447 ptr = tlv_get(result->data, result->length, type, &len);
1451 memcpy(&tmp, ptr, 2);
1454 *value = GUINT16_FROM_LE(tmp);
1459 bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type,
1462 const unsigned char *ptr;
1466 if (!result || !type)
1469 ptr = tlv_get(result->data, result->length, type, &len);
1473 memcpy(&tmp, ptr, 4);
1476 *value = GUINT32_FROM_LE(tmp);
1481 bool qmi_result_get_uint64(struct qmi_result *result, uint8_t type,
1484 const unsigned char *ptr;
1488 if (!result || !type)
1491 ptr = tlv_get(result->data, result->length, type, &len);
1495 memcpy(&tmp, ptr, 8);
1498 *value = GUINT64_FROM_LE(tmp);
1503 struct service_create_data {
1504 struct qmi_device *device;
1509 qmi_create_func_t func;
1511 qmi_destroy_func_t destroy;
1515 static gboolean service_create_reply(gpointer user_data)
1517 struct service_create_data *data = user_data;
1519 data->func(NULL, data->user_data);
1522 data->destroy(data->user_data);
1529 static void service_create_callback(uint16_t message, uint16_t length,
1530 const void *buffer, void *user_data)
1532 struct service_create_data *data = user_data;
1533 struct qmi_device *device = data->device;
1534 struct qmi_service *service = NULL;
1535 const struct qmi_result_code *result_code;
1536 const struct qmi_client_id *client_id;
1538 unsigned int hash_id;
1540 g_source_remove(data->timeout);
1542 result_code = tlv_get(buffer, length, 0x02, &len);
1546 if (len != QMI_RESULT_CODE_SIZE)
1549 client_id = tlv_get(buffer, length, 0x01, &len);
1553 if (len != QMI_CLIENT_ID_SIZE)
1556 if (client_id->service != data->type)
1559 service = g_try_new0(struct qmi_service, 1);
1563 service->ref_count = 1;
1564 service->device = data->device;
1565 service->shared = data->shared;
1567 service->type = data->type;
1568 service->major = data->major;
1569 service->minor = data->minor;
1571 service->client_id = client_id->client;
1573 __debug_device(device, "service created [client=%d,type=%d]",
1574 service->client_id, service->type);
1576 hash_id = service->type | (service->client_id << 8);
1578 g_hash_table_replace(device->service_list,
1579 GUINT_TO_POINTER(hash_id), service);
1582 data->func(service, data->user_data);
1584 qmi_service_unref(service);
1587 data->destroy(data->user_data);
1592 static void service_create_discover(uint8_t count,
1593 const struct qmi_version *list, void *user_data)
1595 struct service_create_data *data = user_data;
1596 struct qmi_device *device = data->device;
1597 struct qmi_request *req;
1598 struct qmi_control_hdr *hdr;
1599 unsigned char client_req[] = { 0x01, 0x01, 0x00, data->type };
1602 __debug_device(device, "service create [type=%d]", data->type);
1604 for (i = 0; i < count; i++) {
1605 if (list[i].type == data->type) {
1606 data->major = list[i].major;
1607 data->minor = list[i].minor;
1612 req = __request_alloc(QMI_SERVICE_CONTROL, 0x00,
1613 QMI_CTL_GET_CLIENT_ID, QMI_CONTROL_HDR_SIZE,
1614 client_req, sizeof(client_req),
1615 service_create_callback, data, (void **) &hdr);
1617 if (data->timeout > 0)
1618 g_source_remove(data->timeout);
1620 g_timeout_add_seconds(0, service_create_reply, data);
1624 if (device->next_control_tid < 1)
1625 device->next_control_tid = 1;
1628 hdr->transaction = device->next_control_tid++;
1630 __request_submit(device, req, hdr->transaction);
1633 static bool service_create(struct qmi_device *device, bool shared,
1634 uint8_t type, qmi_create_func_t func,
1635 void *user_data, qmi_destroy_func_t destroy)
1637 struct service_create_data *data;
1639 data = g_try_new0(struct service_create_data, 1);
1643 data->device = device;
1644 data->shared = shared;
1647 data->user_data = user_data;
1648 data->destroy = destroy;
1650 if (device->version_list) {
1651 service_create_discover(device->version_count,
1652 device->version_list, data);
1656 if (qmi_device_discover(device, service_create_discover, data, NULL))
1664 data->timeout = g_timeout_add_seconds(8, service_create_reply, data);
1669 bool qmi_service_create(struct qmi_device *device,
1670 uint8_t type, qmi_create_func_t func,
1671 void *user_data, qmi_destroy_func_t destroy)
1673 if (!device || !func)
1676 if (type == QMI_SERVICE_CONTROL)
1679 return service_create(device, false, type, func, user_data, destroy);
1682 struct service_create_shared_data {
1683 struct qmi_service *service;
1684 qmi_create_func_t func;
1686 qmi_destroy_func_t destroy;
1689 static gboolean service_create_shared_reply(gpointer user_data)
1691 struct service_create_shared_data *data = user_data;
1693 data->func(data->service, data->user_data);
1695 qmi_service_unref(data->service);
1698 data->destroy(data->user_data);
1705 bool qmi_service_create_shared(struct qmi_device *device,
1706 uint8_t type, qmi_create_func_t func,
1707 void *user_data, qmi_destroy_func_t destroy)
1709 struct qmi_service *service;
1710 unsigned int type_val = type;
1712 if (!device || !func)
1715 if (type == QMI_SERVICE_CONTROL)
1718 service = g_hash_table_find(device->service_list,
1719 __service_compare_shared, GUINT_TO_POINTER(type_val));
1721 struct service_create_shared_data *data;
1723 data = g_try_new0(struct service_create_shared_data, 1);
1727 data->service = qmi_service_ref(service);
1730 data->user_data = user_data;
1731 data->destroy = destroy;
1733 g_timeout_add(0, service_create_shared_reply, data);
1738 return service_create(device, true, type, func, user_data, destroy);
1741 static void service_release_callback(uint16_t message, uint16_t length,
1742 const void *buffer, void *user_data)
1744 struct qmi_service *service = user_data;
1746 if (service->device)
1747 service->device->release_users--;
1752 struct qmi_service *qmi_service_ref(struct qmi_service *service)
1757 __sync_fetch_and_add(&service->ref_count, 1);
1762 void qmi_service_unref(struct qmi_service *service)
1764 unsigned int hash_id;
1769 if (__sync_sub_and_fetch(&service->ref_count, 1))
1772 if (!service->device) {
1777 qmi_service_cancel_all(service);
1778 qmi_service_unregister_all(service);
1780 hash_id = service->type | (service->client_id << 8);
1782 g_hash_table_steal(service->device->service_list,
1783 GUINT_TO_POINTER(hash_id));
1785 service->device->release_users++;
1787 release_client(service->device, service->type, service->client_id,
1788 service_release_callback, service);
1791 const char *qmi_service_get_identifier(struct qmi_service *service)
1796 return __service_type_to_string(service->type);
1799 bool qmi_service_get_version(struct qmi_service *service,
1800 uint16_t *major, uint16_t *minor)
1806 *major = service->major;
1809 *minor = service->minor;
1814 struct service_send_data {
1815 struct qmi_service *service;
1816 struct qmi_param *param;
1817 qmi_result_func_t func;
1819 qmi_destroy_func_t destroy;
1822 static void service_send_free(struct service_send_data *data)
1825 data->destroy(data->user_data);
1827 qmi_param_free(data->param);
1832 static void service_send_callback(uint16_t message, uint16_t length,
1833 const void *buffer, void *user_data)
1835 struct service_send_data *data = user_data;
1836 const struct qmi_result_code *result_code;
1838 struct qmi_result result;
1840 result.message = message;
1841 result.data = buffer;
1842 result.length = length;
1844 result_code = tlv_get(buffer, length, 0x02, &len);
1848 if (len != QMI_RESULT_CODE_SIZE)
1851 result.result = GUINT16_FROM_LE(result_code->result);
1852 result.error = GUINT16_FROM_LE(result_code->error);
1856 data->func(&result, data->user_data);
1858 service_send_free(data);
1861 uint16_t qmi_service_send(struct qmi_service *service,
1862 uint16_t message, struct qmi_param *param,
1863 qmi_result_func_t func,
1864 void *user_data, qmi_destroy_func_t destroy)
1866 struct qmi_device *device;
1867 struct service_send_data *data;
1868 struct qmi_request *req;
1869 struct qmi_service_hdr *hdr;
1874 if (!service->client_id)
1877 device = service->device;
1881 data = g_try_new0(struct service_send_data, 1);
1885 data->service = service;
1886 data->param = param;
1888 data->user_data = user_data;
1889 data->destroy = destroy;
1891 req = __request_alloc(service->type, service->client_id,
1892 message, QMI_SERVICE_HDR_SIZE,
1893 data->param ? data->param->data : NULL,
1894 data->param ? data->param->length : 0,
1895 service_send_callback, data, (void **) &hdr);
1901 if (device->next_service_tid < 256)
1902 device->next_service_tid = 256;
1905 hdr->transaction = device->next_service_tid++;
1907 __request_submit(device, req, hdr->transaction);
1909 return hdr->transaction;
1912 bool qmi_service_cancel(struct qmi_service *service, uint16_t id)
1914 unsigned int tid = id;
1915 struct qmi_device *device;
1916 struct qmi_request *req;
1919 if (!service || !tid)
1922 if (!service->client_id)
1925 device = service->device;
1929 list = g_queue_find_custom(device->req_queue,
1930 GUINT_TO_POINTER(tid), __request_compare);
1934 g_queue_delete_link(device->req_queue, list);
1936 list = g_queue_find_custom(device->service_queue,
1937 GUINT_TO_POINTER(tid), __request_compare);
1943 g_queue_delete_link(device->service_queue, list);
1946 service_send_free(req->user_data);
1948 __request_free(req, NULL);
1953 static GQueue *remove_client(GQueue *queue, uint8_t client)
1958 new_queue = g_queue_new();
1961 struct qmi_request *req;
1963 list = g_queue_pop_head_link(queue);
1969 if (!req->client || req->client != client) {
1970 g_queue_push_tail_link(new_queue, list);
1974 service_send_free(req->user_data);
1976 __request_free(req, NULL);
1979 g_queue_free(queue);
1984 bool qmi_service_cancel_all(struct qmi_service *service)
1986 struct qmi_device *device;
1991 if (!service->client_id)
1994 device = service->device;
1998 device->req_queue = remove_client(device->req_queue,
1999 service->client_id);
2001 device->service_queue = remove_client(device->service_queue,
2002 service->client_id);
2007 uint16_t qmi_service_register(struct qmi_service *service,
2008 uint16_t message, qmi_result_func_t func,
2009 void *user_data, qmi_destroy_func_t destroy)
2011 struct qmi_notify *notify;
2013 if (!service || !func)
2016 notify = g_try_new0(struct qmi_notify, 1);
2020 if (service->next_notify_id < 1)
2021 service->next_notify_id = 1;
2023 notify->id = service->next_notify_id++;
2024 notify->message = message;
2025 notify->callback = func;
2026 notify->user_data = user_data;
2027 notify->destroy = destroy;
2029 service->notify_list = g_list_append(service->notify_list, notify);
2034 bool qmi_service_unregister(struct qmi_service *service, uint16_t id)
2036 unsigned int nid = id;
2037 struct qmi_notify *notify;
2040 if (!service || !id)
2043 list = g_list_find_custom(service->notify_list,
2044 GUINT_TO_POINTER(nid), __notify_compare);
2048 notify = list->data;
2050 service->notify_list = g_list_delete_link(service->notify_list, list);
2052 __notify_free(notify, NULL);
2057 bool qmi_service_unregister_all(struct qmi_service *service)
2062 g_list_foreach(service->notify_list, __notify_free, NULL);
2063 g_list_free(service->notify_list);
2065 service->notify_list = NULL;