3 * oFono - Open Source Telephony
5 * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
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
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/ioctl.h>
42 #define ISIDBG(m, fmt, ...) \
43 if ((m) != NULL && (m)->debug != NULL) \
44 m->debug("gisi: "fmt, ##__VA_ARGS__);
46 struct _GIsiServiceMux {
53 unsigned subscriptions;
54 unsigned registrations;
56 gboolean version_pending;
58 typedef struct _GIsiServiceMux GIsiServiceMux;
76 enum GIsiMessageType type;
77 GIsiServiceMux *service;
80 GIsiNotifyFunc notify;
81 GDestroyNotify destroy;
87 static GIsiServiceMux *service_get(GIsiModem *modem, uint8_t resource)
92 mux = g_hash_table_lookup(modem->services, GINT_TO_POINTER(key));
96 mux = g_try_new0(GIsiServiceMux, 1);
100 g_hash_table_insert(modem->services, GINT_TO_POINTER(key), mux);
103 mux->resource = resource;
104 mux->version.major = -1;
105 mux->version.minor = -1;
106 mux->reachable = FALSE;
107 mux->version_pending = FALSE;
112 static gint utid_equal(gconstpointer a, gconstpointer b)
114 const GIsiPending *pa = a;
115 const GIsiPending *pb = b;
117 return pa->utid - pb->utid;
120 static const char *pend_type_to_str(enum GIsiMessageType type)
123 case GISI_MESSAGE_TYPE_REQ:
125 case GISI_MESSAGE_TYPE_IND:
127 case GISI_MESSAGE_TYPE_NTF:
129 case GISI_MESSAGE_TYPE_RESP:
131 case GISI_MESSAGE_TYPE_COMMON:
137 static void pending_dispatch(GIsiPending *pend, GIsiMessage *msg)
141 if (pend->notify == NULL)
144 modem = pend->service->modem;
146 ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]",
147 g_isi_msg_strerror(msg), pend_type_to_str(pend->type), pend,
148 g_isi_msg_resource(msg), g_isi_msg_id(msg),
149 g_isi_msg_utid(msg));
151 pend->notify(msg, pend->data);
154 static void pending_remove_and_dispatch(GIsiPending *op, GIsiMessage *msg)
158 op->service->pending = g_slist_remove(op->service->pending, op);
160 if (op->notify == NULL || msg == NULL)
163 modem = op->service->modem;
165 ISIDBG(modem, "%s %s to %p [res=0x%02X, id=0x%02X, utid=0x%02X]",
166 g_isi_msg_strerror(msg), pend_type_to_str(op->type), op,
167 g_isi_msg_resource(msg), g_isi_msg_id(msg),
168 g_isi_msg_utid(msg));
170 op->notify(msg, op->data);
174 g_source_remove(op->timeout);
176 if (op->destroy != NULL)
177 op->destroy(op->data);
182 static void service_dispatch(GIsiServiceMux *mux, GIsiMessage *msg,
183 gboolean is_indication)
185 uint8_t msgid = g_isi_msg_id(msg);
186 uint8_t utid = g_isi_msg_utid(msg);
188 GSList *l = mux->pending;
191 GSList *next = l->next;
192 GIsiPending *pend = l->data;
195 * REQs, NTFs and INDs are dispatched on message ID. While
196 * INDs have the unique transaction ID set to zero, NTFs
197 * typically mirror the UTID of the request that set up the
198 * session, and REQs can naturally have any transaction ID.
200 * RESPs are dispatched on unique transaction ID, explicitly
201 * ignoring the msgid. A RESP also completes a transaction,
202 * so it needs to be removed after being notified of.
204 * Version query responses are dispatched in a similar fashion
205 * as RESPs, but based on the pending type and the message ID.
206 * Some of these may be synthesized, but nevertheless need to
209 if (pend->type < GISI_MESSAGE_TYPE_RESP
210 && pend->msgid == msgid) {
212 pending_dispatch(pend, msg);
214 } else if (pend->type == GISI_MESSAGE_TYPE_RESP &&
215 !is_indication && pend->utid == utid) {
217 pending_remove_and_dispatch(pend, msg);
220 } else if (pend->type == GISI_MESSAGE_TYPE_COMMON &&
221 msgid == COMMON_MESSAGE &&
222 pend->msgid == COMM_ISI_VERSION_GET_REQ) {
224 pending_remove_and_dispatch(pend, msg);
231 static void common_message_decode(GIsiServiceMux *mux, GIsiMessage *msg)
237 if (!g_isi_msg_data_get_byte(msg, 0, &code))
241 case COMM_ISA_ENTITY_NOT_REACHABLE_RESP:
242 mux->reachable = FALSE;
246 case COMM_ISI_VERSION_GET_RESP:
248 if (g_isi_msg_data_get_byte(msg, 1, &major) &&
249 g_isi_msg_data_get_byte(msg, 2, &minor)) {
250 mux->version.major = major;
251 mux->version.minor = minor;
257 * PN_SIM doesn't support ISI version, but sends a
258 * 0x00 message as a response. Work around this modem
261 mux->object = g_isi_msg_object(msg);
262 mux->version_pending = FALSE;
263 mux->reachable = TRUE;
266 msg->version = &mux->version;
269 static void firewall_notify_handle(GIsiModem *modem, GIsiMessage *msg)
273 if (!g_isi_msg_data_get_byte(msg, 0, &id))
276 ISIDBG(modem, "firewall blocked message 0x%02X", id);
279 static gboolean isi_callback(GIOChannel *channel, GIOCondition cond,
282 GIsiModem *modem = data;
286 if (cond & (G_IO_NVAL|G_IO_HUP)) {
287 ISIDBG(modem, "Unexpected event on PhoNet channel %p", channel);
291 fd = g_io_channel_unix_get_fd(channel);
292 len = g_isi_phonet_peek_length(channel);
295 struct sockaddr_pn addr;
296 uint32_t buf[(len + 3) / 4];
302 len = g_isi_phonet_read(channel, buf, len, &addr);
311 if (modem->trace != NULL)
312 modem->trace(&msg, NULL);
314 key = addr.spn_resource;
315 mux = g_hash_table_lookup(modem->services,
316 GINT_TO_POINTER(key));
319 * Unfortunately, the FW report has the wrong
320 * resource ID in the N900 modem.
322 if (key == PN_FIREWALL)
323 firewall_notify_handle(modem, &msg);
328 msg.version = &mux->version;
330 if (g_isi_msg_id(&msg) == COMMON_MESSAGE)
331 common_message_decode(mux, &msg);
333 service_dispatch(mux, &msg, fd == modem->ind_fd);
338 static gboolean modem_subs_update(gpointer data)
341 gpointer keyptr, value;
343 GIsiModem *modem = data;
344 struct sockaddr_pn commgr = {
345 .spn_family = AF_PHONET,
346 .spn_resource = PN_COMMGR,
347 .spn_dev = modem->device,
349 uint8_t msg[3 + 256] = {
350 0, PNS_SUBSCRIBED_RESOURCES_IND,
355 modem->subs_source = 0;
357 g_hash_table_iter_init(&iter, modem->services);
359 while (g_hash_table_iter_next(&iter, &keyptr, &value)) {
360 GIsiServiceMux *mux = value;
362 if (mux->subscriptions > 0) {
363 msg[3 + count] = mux->resource;
369 sendto(modem->ind_fd, msg, 3 + msg[2], MSG_NOSIGNAL, (void *)&commgr,
375 static void modem_subs_update_when_idle(GIsiModem *modem)
377 if (modem->subs_source > 0)
380 modem->subs_source = g_idle_add(modem_subs_update, modem);
383 static void service_name_register(GIsiServiceMux *mux)
385 struct sockaddr_pn namesrv = {
386 .spn_family = AF_PHONET,
387 .spn_resource = PN_NAMESERVICE,
388 .spn_dev = mux->modem->device,
391 0, PNS_NAME_ADD_REQ, 0, 0,
392 0, 0, 0, mux->resource, /* 32-bit Big-Endian name */
393 0, 0, /* device/object */
398 if (ioctl(mux->modem->req_fd, SIOCPNGETOBJECT, &object) < 0) {
399 ISIDBG(mux->modem, "ioctl(SIOCPNGETOBJECT): %s",
404 /* Fill in the object ID */
405 msg[8] = object >> 8;
406 msg[9] = object & 0xFF;
408 sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
409 (void *)&namesrv, sizeof(namesrv));
412 static void service_name_deregister(GIsiServiceMux *mux)
414 struct sockaddr_pn namesrv = {
415 .spn_family = AF_PHONET,
416 .spn_resource = PN_NAMESERVICE,
417 .spn_dev = mux->modem->device,
419 const uint8_t msg[] = {
420 0, PNS_NAME_REMOVE_REQ, 0, 0,
421 0, 0, 0, mux->resource,
424 sendto(mux->modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
425 (void *)&namesrv, sizeof(namesrv));
428 static void pending_destroy(gpointer value, gpointer user)
430 GIsiPending *op = value;
436 g_source_remove(op->timeout);
438 if (op->destroy != NULL)
439 op->destroy(op->data);
444 static void service_finalize(gpointer value)
446 GIsiServiceMux *mux = value;
447 GIsiModem *modem = mux->modem;
449 if (mux->subscriptions > 0)
450 modem_subs_update_when_idle(modem);
452 if (mux->registrations > 0)
453 service_name_deregister(mux);
455 g_slist_foreach(mux->pending, pending_destroy, NULL);
456 g_slist_free(mux->pending);
460 GIsiModem *g_isi_modem_create(unsigned index)
471 modem = g_try_new0(GIsiModem, 1);
477 inds = g_isi_phonet_new(index);
478 reqs = g_isi_phonet_new(index);
480 if (inds == NULL || reqs == NULL) {
485 modem->req_fd = g_io_channel_unix_get_fd(reqs);
486 modem->req_watch = g_io_add_watch(reqs,
487 G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
488 isi_callback, modem);
489 modem->ind_fd = g_io_channel_unix_get_fd(inds);
490 modem->ind_watch = g_io_add_watch(inds,
491 G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
492 isi_callback, modem);
494 g_io_channel_unref(reqs);
495 g_io_channel_unref(inds);
497 modem->index = index;
498 modem->services = g_hash_table_new_full(g_direct_hash, NULL,
499 NULL, service_finalize);
504 GIsiModem *g_isi_modem_create_by_name(const char *name)
506 return g_isi_modem_create(if_nametoindex(name));
509 void *g_isi_modem_set_userdata(GIsiModem *modem, void *data)
517 modem->opaque = data;
522 void *g_isi_modem_get_userdata(GIsiModem *modem)
527 return modem->opaque;
530 unsigned long g_isi_modem_flags(GIsiModem *modem)
538 void g_isi_modem_set_flags(GIsiModem *modem, unsigned long flags)
543 modem->flags = flags;
546 uint8_t g_isi_modem_device(GIsiModem *modem)
551 return modem->device;
554 int g_isi_modem_set_device(GIsiModem *modem, uint8_t remote)
559 if (remote != PN_DEV_HOST && remote != PN_DEV_MODEM)
562 modem->device = remote;
567 static uint8_t service_next_utid(GIsiServiceMux *mux)
569 if (mux->last_utid == 0x00 || mux->last_utid == 0xFF)
572 return mux->last_utid + 1;
575 static void service_subs_incr(GIsiServiceMux *mux)
577 GIsiModem *modem = mux->modem;
579 mux->subscriptions++;
581 if (mux->subscriptions == 1)
582 modem_subs_update_when_idle(modem);
585 static void service_subs_decr(GIsiServiceMux *mux)
587 GIsiModem *modem = mux->modem;
589 if (mux->subscriptions == 0)
592 mux->subscriptions--;
594 if (mux->subscriptions == 0)
595 modem_subs_update_when_idle(modem);
598 static void service_regs_incr(GIsiServiceMux *mux)
600 mux->registrations++;
602 if (mux->registrations == 1)
603 service_name_register(mux);
606 static void service_regs_decr(GIsiServiceMux *mux)
608 if (mux->registrations == 0)
611 mux->registrations--;
613 if (mux->registrations == 0)
614 service_name_deregister(mux);
617 void g_isi_modem_destroy(GIsiModem *modem)
622 g_hash_table_remove_all(modem->services);
624 if (modem->subs_source > 0) {
625 g_source_remove(modem->subs_source);
626 modem_subs_update(modem);
629 g_hash_table_unref(modem->services);
631 if (modem->ind_watch > 0)
632 g_source_remove(modem->ind_watch);
634 if (modem->req_watch > 0)
635 g_source_remove(modem->req_watch);
640 unsigned g_isi_modem_index(GIsiModem *modem)
642 return modem != NULL ? modem->index : 0;
645 GIsiPending *g_isi_request_send(GIsiModem *modem, uint8_t resource,
646 const void *__restrict buf, size_t len,
647 unsigned timeout, GIsiNotifyFunc notify,
648 void *data, GDestroyNotify destroy)
650 struct sockaddr_pn dst = {
651 .spn_family = AF_PHONET,
652 .spn_resource = resource,
653 .spn_dev = modem->device,
656 return g_isi_request_sendto(modem, &dst, buf, len, timeout, notify,
660 GIsiPending *g_isi_request_vsend(GIsiModem *modem, uint8_t resource,
661 const struct iovec *__restrict iov,
662 size_t iovlen, unsigned timeout,
663 GIsiNotifyFunc notify, void *data,
664 GDestroyNotify destroy)
666 struct sockaddr_pn dst = {
667 .spn_family = AF_PHONET,
668 .spn_resource = resource,
669 .spn_dev = modem->device,
672 return g_isi_request_vsendto(modem, &dst, iov, iovlen, timeout, notify,
676 GIsiPending *g_isi_request_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
677 const void *__restrict buf, size_t len,
678 unsigned timeout, GIsiNotifyFunc notify,
679 void *data, GDestroyNotify destroy)
681 const struct iovec iov = {
682 .iov_base = (void *)buf,
686 return g_isi_request_vsendto(modem, dst, &iov, 1, timeout, notify, data,
690 static void vtrace(struct sockaddr_pn *dst,
691 const struct iovec *__restrict iov, size_t iovlen,
692 size_t total_len, GIsiNotifyFunc trace)
694 uint8_t buffer[total_len];
695 uint8_t *ptr = buffer;
698 .data = (const void *)buffer,
703 for (i = 0; i < iovlen; i++) {
704 memcpy(ptr, iov[i].iov_base, iov[i].iov_len);
705 ptr += iov[i].iov_len;
711 static gboolean resp_timeout(gpointer data)
713 GIsiPending *op = data;
720 pending_remove_and_dispatch(op, &msg);
725 GIsiPending *g_isi_request_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
726 const struct iovec *__restrict iov,
727 size_t iovlen, unsigned timeout,
728 GIsiNotifyFunc notify, void *data,
729 GDestroyNotify destroy)
731 struct iovec _iov[1 + iovlen];
732 struct msghdr msg = {
733 .msg_name = (void *)dst,
734 .msg_namelen = sizeof(struct sockaddr_pn),
736 .msg_iovlen = 1 + iovlen,
752 mux = service_get(modem, dst->spn_resource);
758 resp = g_try_new0(GIsiPending, 1);
764 resp->type = GISI_MESSAGE_TYPE_RESP;
765 resp->utid = service_next_utid(mux);
767 resp->notify = notify;
768 resp->destroy = destroy;
771 if (g_slist_find_custom(mux->pending, resp, utid_equal)) {
773 * FIXME: perhaps retry with randomized access after
774 * initial miss. Although if the rate at which
775 * requests are sent is so high that the unique
776 * transaction ID wraps, it's likely there is
777 * something wrong and we might as well fail here.
779 ISIDBG(modem, "ERROR: UTID wrapped, modem busy");
784 _iov[0].iov_base = &resp->utid;
787 for (i = 0, len = 1; i < iovlen; i++) {
788 _iov[1 + i] = iov[i];
789 len += iov[i].iov_len;
792 if (modem->trace != NULL)
793 vtrace(dst, _iov, 1 + iovlen, len, modem->trace);
795 ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL);
799 if (ret != (ssize_t)len) {
804 mux->pending = g_slist_prepend(mux->pending, resp);
807 resp->timeout = g_timeout_add_seconds(timeout, resp_timeout,
810 mux->last_utid = resp->utid;
818 uint8_t g_isi_request_utid(GIsiPending *resp)
820 return resp != NULL ? resp->utid : 0;
823 void g_isi_pending_remove(GIsiPending *op)
828 if (op->type == GISI_MESSAGE_TYPE_IND)
829 service_subs_decr(op->service);
831 if (op->type == GISI_MESSAGE_TYPE_REQ)
832 service_regs_decr(op->service);
834 if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) {
839 pending_remove_and_dispatch(op, &msg);
843 op->service->pending = g_slist_remove(op->service->pending, op);
845 pending_destroy(op, NULL);
848 static void foreach_destroy(GIsiPending *op)
850 if (op->type == GISI_MESSAGE_TYPE_IND)
851 service_subs_decr(op->service);
853 if (op->type == GISI_MESSAGE_TYPE_REQ)
854 service_regs_decr(op->service);
856 if (op->type == GISI_MESSAGE_TYPE_RESP && op->notify != NULL) {
861 pending_dispatch(op, &msg);
864 pending_destroy(op, NULL);
867 void g_isi_pending_set_owner(GIsiPending *op, gpointer owner)
875 void g_isi_remove_pending_by_owner(GIsiModem *modem, uint8_t resource,
882 GSList *owned = NULL;
884 mux = service_get(modem, resource);
888 for (l = mux->pending; l != NULL; l = next) {
892 if (op->owner != owner)
895 mux->pending = g_slist_remove_link(mux->pending, l);
901 for (l = owned; l != NULL; l = l->next) {
910 GIsiPending *g_isi_ntf_subscribe(GIsiModem *modem, uint8_t resource,
911 uint8_t msgid, GIsiNotifyFunc notify,
912 void *data, GDestroyNotify destroy)
917 mux = service_get(modem, resource);
923 ntf = g_try_new0(GIsiPending, 1);
929 ntf->type = GISI_MESSAGE_TYPE_NTF;
931 ntf->notify = notify;
933 ntf->destroy = destroy;
936 mux->pending = g_slist_append(mux->pending, ntf);
938 ISIDBG(modem, "Subscribed to %s (%p) [res=0x%02X, id=0x%02X]",
939 pend_type_to_str(ntf->type), ntf, resource, msgid);
944 GIsiPending *g_isi_service_bind(GIsiModem *modem, uint8_t resource,
945 uint8_t msgid, GIsiNotifyFunc notify,
946 void *data, GDestroyNotify destroy)
951 mux = service_get(modem, resource);
957 srv = g_try_new0(GIsiPending, 1);
963 srv->type = GISI_MESSAGE_TYPE_REQ;
965 srv->notify = notify;
967 srv->destroy = destroy;
970 mux->pending = g_slist_append(mux->pending, srv);
972 ISIDBG(modem, "Bound service for %s (%p) [res=0x%02X, id=0x%02X]",
973 pend_type_to_str(srv->type), srv, resource, msgid);
975 service_regs_incr(mux);
980 GIsiPending *g_isi_ind_subscribe(GIsiModem *modem, uint8_t resource,
981 uint8_t msgid, GIsiNotifyFunc notify,
982 void *data, GDestroyNotify destroy)
987 mux = service_get(modem, resource);
993 ind = g_try_new0(GIsiPending, 1);
999 ind->type = GISI_MESSAGE_TYPE_IND;
1001 ind->notify = notify;
1003 ind->destroy = destroy;
1006 mux->pending = g_slist_append(mux->pending, ind);
1008 ISIDBG(modem, "Subscribed for %s (%p) [res=0x%02X, id=0x%02X]",
1009 pend_type_to_str(ind->type), ind, resource, msgid);
1011 service_subs_incr(mux);
1016 int g_isi_response_send(GIsiModem *modem, const GIsiMessage *req,
1017 const void *__restrict buf, size_t len)
1019 const struct iovec iov = {
1020 .iov_base = (void *)buf,
1024 return g_isi_response_vsend(modem, req, &iov, 1);
1027 int g_isi_response_vsend(GIsiModem *modem, const GIsiMessage *req,
1028 const struct iovec *__restrict iov,
1031 struct iovec _iov[1 + iovlen];
1035 utid = g_isi_msg_utid(req);
1037 _iov[0].iov_base = &utid;
1038 _iov[0].iov_len = 1;
1040 for (i = 0; i < iovlen; i++)
1041 _iov[1 + i] = iov[i];
1043 return g_isi_modem_vsendto(modem, req->addr, _iov, 1 + iovlen);
1046 int g_isi_modem_send(GIsiModem *modem, uint8_t resource,
1047 const void *__restrict buf, size_t len)
1049 struct sockaddr_pn dst = {
1050 .spn_family = AF_PHONET,
1051 .spn_resource = resource,
1052 .spn_dev = modem->device,
1055 return g_isi_modem_sendto(modem, &dst, buf, len);
1058 int g_isi_modem_vsend(GIsiModem *modem, uint8_t resource,
1059 const struct iovec *__restrict iov,
1062 struct sockaddr_pn dst = {
1063 .spn_family = AF_PHONET,
1064 .spn_resource = resource,
1065 .spn_dev = modem->device,
1068 return g_isi_modem_vsendto(modem, &dst, iov, iovlen);
1071 int g_isi_modem_sendto(GIsiModem *modem, struct sockaddr_pn *dst,
1072 const void *__restrict buf, size_t len)
1074 const struct iovec iov = {
1075 .iov_base = (void *)buf,
1079 return g_isi_modem_vsendto(modem, dst, &iov, 1);
1082 int g_isi_modem_vsendto(GIsiModem *modem, struct sockaddr_pn *dst,
1083 const struct iovec *__restrict iov,
1086 struct msghdr msg = {
1087 .msg_name = (void *)dst,
1088 .msg_namelen = sizeof(struct sockaddr_pn),
1089 .msg_iov = (struct iovec *)iov,
1090 .msg_iovlen = iovlen,
1091 .msg_control = NULL,
1092 .msg_controllen = 0,
1097 GIsiServiceMux *mux;
1102 mux = service_get(modem, dst->spn_resource);
1106 for (i = 0, len = 0; i < iovlen; i++)
1107 len += iov[i].iov_len;
1109 if (modem->trace != NULL)
1110 vtrace(dst, iov, iovlen, len, modem->trace);
1112 ret = sendmsg(modem->req_fd, &msg, MSG_NOSIGNAL);
1116 if (ret != (ssize_t)len)
1122 void g_isi_modem_set_trace(GIsiModem *modem, GIsiNotifyFunc trace)
1127 modem->trace = trace;
1130 void g_isi_modem_set_debug(GIsiModem *modem, GIsiDebugFunc debug)
1135 modem->debug = debug;
1138 static int version_get_send(GIsiModem *modem, GIsiPending *ping)
1140 GIsiServiceMux *mux = ping->service;
1141 struct sockaddr_pn dst = {
1142 .spn_family = AF_PHONET,
1143 .spn_resource = mux->resource,
1144 .spn_dev = modem->device,
1147 ping->utid, /* UTID */
1149 COMM_ISI_VERSION_GET_REQ,
1154 if (g_slist_find_custom(mux->pending, ping, utid_equal))
1157 ret = sendto(modem->req_fd, msg, sizeof(msg), MSG_NOSIGNAL,
1158 (void *)&dst, sizeof(dst));
1163 if (ret != (ssize_t)sizeof(msg))
1166 mux->last_utid = ping->utid;
1167 mux->version_pending = TRUE;
1171 static gboolean reachable_notify(gpointer data)
1173 GIsiPending *pong = data;
1174 GIsiServiceMux *mux = pong->service;
1176 struct sockaddr_pn addr = {
1177 .spn_resource = mux->resource,
1178 .spn_dev = mux->object >> 8,
1179 .spn_obj = mux->object & 0xff,
1182 .version = &mux->version,
1186 pending_remove_and_dispatch(pong, &msg);
1191 GIsiPending *g_isi_resource_ping(GIsiModem *modem, uint8_t resource,
1192 GIsiNotifyFunc notify, void *data,
1193 GDestroyNotify destroy)
1195 GIsiServiceMux *mux;
1199 mux = service_get(modem, resource);
1205 ping = g_try_new0(GIsiPending, 1);
1211 ping->type = GISI_MESSAGE_TYPE_COMMON;
1212 ping->utid = service_next_utid(mux);
1213 ping->service = mux;
1214 ping->notify = notify;
1216 ping->destroy = destroy;
1217 ping->msgid = COMM_ISI_VERSION_GET_REQ;
1219 if (mux->reachable) {
1220 g_idle_add(reachable_notify, ping);
1224 if (!mux->version_pending) {
1225 ret = version_get_send(modem, ping);
1231 mux->last_utid = ping->utid;
1234 ping->timeout = g_timeout_add_seconds(COMMON_TIMEOUT, resp_timeout,
1236 mux->pending = g_slist_prepend(mux->pending, ping);
1237 mux->version_pending = TRUE;
1239 ISIDBG(modem, "Ping sent %s (%p) [res=0x%02X]",
1240 pend_type_to_str(ping->type), ping, resource);